thymeleaf test case spring boot - spring

#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class ShopControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private UserService userService;
#Test
public void showRegistrationForm() throws Exception {
String uuid = UUID.randomUUID().toString();
when(userService.showRegistrationForm(uuid)).thenReturn(new UserRegistration());
this.mockMvc.perform(get("/user/registration/"+uuid))
.andExpect(status().isOk()).andExpect(view()
.name("registrationForm"))
.andExpect(model().attributeExists("data"))
.andExpect(model().attributeExists("reqDto"))
.andExpect(model().attributeExists(uuid));
}
}
I wrote this unit test to test the thymeleaf template. I am not able to run this because the UUID generated does not exist in the database.
This is the controller:
#RequestMapping(value = "/registration/{uuid}")
public String showRegistrationForm(#PathVariable String uuid, Model model) {
try {
UserRegistration UserRegistration = this.userService.showRegistrationForm(uuid);
model.addAttribute("user", true);
model.addAttribute("uuid", uuid);
model.addAttribute("reqDto", new RequestDto());
} catch (UrlExpiredException e) {
System.out.println("UrlExpiredException");
model.addAttribute("exception", e.getMessage());
} catch (UrlNotFoundException e) {
System.out.println("UrlNotFoundException");
model.addAttribute("exception", e.getMessage());
}
return "registrationForm";
}
this is in pom.xml
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
What am I missing?

Related

the #Mock and #InjectMocks annotations don't work

rest
#RestController
public class StackOverFlowController {
private StackOverFlowService service;
#Autowired
public StackOverFlowController(
#Qualifier(value = "stackOverFlowServiceImpl")
StackOverFlowService service) {
this.service = service;
}
#RequestMapping("api/stackoverflow")
public List<StackOverFlowDto> getListOfProviders() throws URISyntaxException {
List<StackOverFlowDto> allSites = service.getAllSites();
return allSites;
}
}
service
public interface StackOverFlowService {
List<StackOverFlowDto> getAllSites() throws URISyntaxException;
List<StackOverFlowDto> getAllSitesByTitle(String title) throws URISyntaxException;
}
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${version.mapstruct}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
test
#RunWith(MockitoJUnitRunner.class)
public class StackOverFlowControllerTest {
#Mock
private StackOverFlowService service ;
#InjectMocks
private StackOverFlowController controller;
#Test
public void getListOfProviders() throws URISyntaxException {
List<StackOverFlowDto> allSites = service.getAllSites();
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
verify(service).getAllSites();
}
}
I can create an mock-object this way: :
private StackOverFlowService service = Mockito.mock(StackOverFlowService.class);
update 1
update 2
Wnen I have done this:
#RunWith(SpringRunner.class)
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTest {
#Mock
private StackOverFlowService service;
#InjectMocks
StackOverFlowController controller;
#Test
public void getListOfProviders() throws URISyntaxException {
List<StackOverFlowDto> allSites = service.getAllSites();
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
verify(service).getAllSites();
}
}
I got an error:
PM org.junit.vintage.engine.descriptor.RunnerTestDescriptor warnAboutUnfilterableRunner WARNING: Runner
org.junit.internal.runners.ErrorReportingRunner (used on class
com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest)
does not support filtering and will therefore be run completely.
org.junit.runners.model.InvalidTestClassError: Invalid test class
'com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest':
No runnable methods
where is the method get() from ?
update 3
This the test was performed:
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#MockBean
private StackOverFlowController controller;
#Test
public void getListOfProviders() throws Exception {
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
assertNotNull(listOfProviders);
}
}
But I don't see the point. I don't get any data:
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
But here I should get a collection of elements, but I get 0
Update 4
It is my the understanding:
when(service.getAllSites()).thenReturn(ImmutableList.of());
We test a controller. We don't need below layers. We need to validate that the method
getListOfProviders()
of a contoller is called.
So,
service.getAllSites()
here we point Mockito-object
#MockBean
private StackOverFlowService service;
This method should return some sort of collection.
thenReturn(ImmutableList.of())
it should return empty collection.
Why ?
I don't understand this.
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
What is it for?
Update 5
#Test
public void getListOfProviders() throws Exception {
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
assertNotNull(listOfProviders);
verify(service).getAllSites();
}
verify(service).getAllSites();
This should validate that the method getAllSites() from interface StackOverFlowService (from its implemetation), will call...
But Again I got errors:
Wanted but not invoked:
com.spring.mongo.service.read.stackoverflow.StackOverFlowService#0
bean.getAllSites();
-> at com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest.getListOfProviders(StackOverFlowControllerTest.java:38)
Actually, there were zero interactions with this mock.
Wanted but not invoked:
com.spring.mongo.service.read.stackoverflow.StackOverFlowService#0
bean.getAllSites();
-> at com.spring.mongo.web.contollers.stackoverflow.StackOverFlowControllerTest.getListOfProviders(StackOverFlowControllerTest.java:38)
Actually, there were zero interactions with this mock.
Why ?
Update 6
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoApplication.class})
#WebMvcTest(StackOverFlowController.class)
class StackOverFlowControllerTestUnit {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#MockBean
private StackOverFlowController controller;
#Test
void getListOfProviders() throws Exception {
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders
.get("/api/stackoverflow")
.accept(MediaType.APPLICATION_JSON);
mockMvc.perform(requestBuilder)
.andDo(print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.id").exists())
.andDo(print());
}
}
Here beans is created, but i don't result into json...
.andExpect(MockMvcResultMatchers.jsonPath("$.sites").exists())
.andExpect(MockMvcResultMatchers.jsonPath("$.sites[*].id").isNotEmpty());
Update_7
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoApplication.class})
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTestFromForum {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#InjectMocks
private StackOverFlowController controller;
#Test
public void getListOfProviders() throws Exception {
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders
.get("/api/stackoverflow")
.accept(MediaType.APPLICATION_JSON);
mockMvc.perform(requestBuilder)
.andDo(print())
.andExpect(status().isOk());
}
}
In this version of the code, all beans are initialized without errors.
#InjectMocks - It is work also.
Normal integration testing offered by Spring simply creates objects
as if the application were running in production. I still don't
understand what Mock-objects are for.
Here any ideas is finished.
I don't understand why it dosen't work.
Сan someone explain the idea of #Mock and #InjectMocks annotations working in relation to this example ?
Does anyone have any ideas?
If you want the controller test to be the same like any other unit test case class then use spring for running tests using annotation #RunWith(SpringRunner.class) .Since you are writing the unit test case for the controller , use the test method like below.You just need to mock the service call and call the controller method or more appropriately hit it via mock mvc :
#Test
public void getListOfProviders() throws URISyntaxException {
when(service.getAllSites()).thenReturn(ImmutableList.of());
List<StackOverFlowDto> listOfProviders = controller.getListOfProviders();
assertNotNull(listOfProviders);
}
But if you want it to be the right way then :
#WebMvcTest
public class StackOverFlowControllerTest {
#Mock
private StackOverFlowService service ;
#Autowired
private MockMvc mockMvc;
#Test
public void getListOfProviders() throws URISyntaxException {
when(service.getAllSites()).thenReturn(ImmutableList.of());
this.mockMvc.perform(get("api/stackoverflow")).andDo(print()).andExpect(status().isOk());
}
}
If you are having issues ith #WebMvcTest it means springboot is having trouble setting up the test env because of project structure or annotation on main application class that might be causing issues for test setup so in that case try setting up an entire application context like in an integration test (a bit tedious and unnecessary ) by using the #SpringBootTest annotation instead.
Official doc link.
Try with #RunWith(SpringRunner.class) instead of #RunWith(MockitoJUnitRunner.class), it should work. SpringRunner will help with Dependency Injection of the mocks.
Edit:
Try with the code below:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoApplication.class})
#WebMvcTest(StackOverFlowController.class)
public class StackOverFlowControllerTestUnit {
#Autowired
private MockMvc mockMvc;
#MockBean
private StackOverFlowService service;
#Test
void getListOfProviders() throws Exception {
MockHttpServletRequestBuilder requestBuilder =
MockMvcRequestBuilders
.get("/api/stackoverflow")
.accept(MediaType.APPLICATION_JSON);
mockMvc.perform(requestBuilder)
.andDo(print())
.andExpect(status().isOk());
// .andExpect(MockMvcResultMatchers.jsonPath("$.id").exists())
// .andDo(print());
}
}
Try going step by step. If the above works then maybe you can go for the json path verification

Not able to save data in H2 Database using SpringBootTest and Spring JPA Repository

I am using #SpringBootTest to test SpringSecurity basic authentication.When I test it,the h2 database does not save the data.I do not see the insert statement in the console,which I apparently see when I am running my actual SpringBoot Application and inserting the data from frontend. Please help.
Below is my test:
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
#ContextConfiguration(classes=ConnectionsAppApplication.class)
#Transactional
public class AuthenticationTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Mock
CustDetailsRepository custRepository;
#Mock
BCryptPasswordEncoder encrypt;
#InjectMocks
CustomerServiceImpl customerServiceImpl;
#BeforeEach
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
void testAuthentication() {
CustomerDetails customer = new CustomerDetails();
customer.setEmailid("abc.com");
customer.setPassword("abc#123456");
customerServiceImpl.saveUser(customer);
try {
this.mockMvc.perform(get("/api/login")
.with(httpBasic("abc.com","abc#123456")))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
} catch (Exception e) {
e.printStackTrace();
}
}
}
saveUser method in CustomerServiceImpl class:
public void saveUser(CustomerDetails customerDetails) {
customerDetails.setPassword(bCryptPasswordEncoder.encode(customerDetails.getPassword()));
custDetailsRepository.save(customerDetails);
}
You have 2 options to implement this test:
Option 1: use real h2
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
#ContextConfiguration(classes=ConnectionsAppApplication.class)
#Transactional
public class AuthenticationTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Autowired
CustomerServiceImpl customerServiceImpl;
#BeforeEach
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
void testAuthentication() {
CustomerDetails customer = new CustomerDetails();
customer.setEmailid("abc.com");
customer.setPassword("abc#123456");
customerServiceImpl.saveUser(customer);
try {
this.mockMvc.perform(get("/api/login")
.with(httpBasic("abc.com","abc#123456")))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Option 2: Mock your service / repository
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
#ContextConfiguration(classes=ConnectionsAppApplication.class)
#Transactional
public class AuthenticationTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#MockBean
CustomerServiceImpl customerServiceImpl;
#BeforeEach
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
void testAuthentication() {
// set expectations on CustomerServiceImpl
CustomerDetails customer = new CustomerDetails();
customer.setEmailid("abc.com");
customer.setPassword("abc#123456");
// mock the method you use to fetch the customer
when(customerServiceImpl.getUser("abc.com").thenReturn(customer);
try {
this.mockMvc.perform(get("/api/login")
.with(httpBasic("abc.com","abc#123456")))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Note that you also can use #WebMvcTest to test only the web slice of your app (meaning no other beans will be instantiated, for example all sercies you depend in the controller must be delivered by #MockBean)

How to bypass the Spring #PreAuthorize annotation on RestController for tests?

How can I create a bypass #Preauthorize so that I can test in local with out calling the actual because annotation will be loaded before the class loads ?
#RestController
#RequestMapping("/test")
public class ResourceController {
#RequestMapping(method = GET)
#PreAuthorize
#ResponseBody
public String message(){ return "Hello World"; }
You can use spring-security-test to achieve this.
pom.xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>5.3.3.RELEASE</version>
<scope>test</scope>
</dependency>
Supposedly your controller looks like:
#PreAuthorize("hasRole('ROLE_ADMIN')")
public User createUser(final User user) {
......
}
And your test can look like:
public class MyControllerTests {
private MockMvc mvc;
#BeforeEach
void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
void testCreateWithProperPermission() throws Exception {
final User user = new User();
user.setName("Test");
final MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.post("/v1/foo/").with(user("foo").roles("ADMIN"))
.content(new ObjectMapper().writeValueAsString(user))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
final String responseBody = mvcResult.getResponse().getContentAsString();
final User created = new ObjectMapper().readValue(responseBody, User.class);
// verify the saved entity's data is correct
assertThat(created).isNotNull();
assertThat(created)
.hasFieldOrPropertyWithValue("name", user.getName());
}
You can have a test profile in your code which you then activate when running tests against the code. You can then use the predefined user and password in your tests.
#Configuration
public class TestConfig {
#EnableWebSecurity
#Profile("test")
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("ROLE1", "ROLE2", "ROLE3").build());
return manager;
}
}
}

Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type:

I am trying to expose REST service, but while hitting it from POSTMAN i am getting below :
WARNING: Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class java.util.ArrayList
Where as i have also included below jar files which are required :
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.1</version>
</dependency>
Here is my REST controller Code :
#RestController
#RequestMapping("/MayankAPI")
public class TestRestAPI {
#RequestMapping(value="/sayHello" , method = RequestMethod.POST)
public TestPojo postData(#RequestBody String payload) {
System.out.println("Hello post"+payload);
TestPojo payload1=new TestPojo();
payload1.setStudentName("Jack");
return payload1;
}
#RequestMapping(value="/sayHello" , method = RequestMethod.GET)
public List<TestPojo> getData(String payload) {
System.out.println("Hello get"+payload);
List<TestPojo> payload1=new ArrayList<TestPojo>();
TestPojo tp = new TestPojo();
tp.setStudentName("Jack");
payload1.add(tp);
return payload1;
}
Here is my bean which i am trying to return :
public class TestPojo {
private String studentName;
private String studentId;
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
}
Please help me where i am doing wrong.
I know it too late but still
Alternate Solution:
you need to enable Spring project as Web MVC as follow:
#Configuration
#EnableWebMvc
#ComponentScan("basePackages = com.test")
public class MiBenefitConfiguration
{
}
This is happening because MappingJackson2HttpMessageConverter is not registered in my App config file as below.
#Configuration
#ComponentScan("basePackages = com.test")
public class MiBenefitConfiguration extends WebMvcConfigurationSupport{
#Bean
public ObjectMapper getObjectMapper() {
return new ObjectMapper();
}
#Bean
public MappingJackson2HttpMessageConverter messageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(getObjectMapper());
return converter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(messageConverter());
addDefaultHttpMessageConverters(converters);
}
}

Mocked Spring #Service that has #Retryable annotations on methods fails with UnfinishedVerificationException

I'm using Spring Boot 1.4.0.RELEASE with spring-boot-starter-batch, spring-boot-starter-aop and spring-retry
I have a Spring Integration test that has a #Service which is mocked at runtime. I've noticed that if the #Service class contains any #Retryable annotations on its methods, then it appears to interfere with Mockito.verify(), I get a UnfinishedVerificationException. I presume this must be something to do with spring-aop? If I comment out all #Retryable annotations in the #Service then verify works ok again.
I have created a github project that demonstrates this issue.
It fails in sample.batch.MockBatchTestWithRetryVerificationFailures.batchTest() at validateMockitoUsage();
With something like:
12:05:36.554 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test method: context [DefaultTestContext#5ec0a365 testClass = MockBatchTestWithRetryVerificationFailures, testInstance = sample.batch.MockBatchTestWithRetryVerificationFailures#5abca1e0, testMethod = batchTest#MockBatchTestWithRetryVerificationFailures, testException = org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at sample.batch.service.MyRetryService$$FastClassBySpringCGLIB$$7573ce2a.invoke(<generated>)
Example of correct verification:
verify(mock).doSomething()
However I have another class (sample.batch.MockBatchTestWithNoRetryWorking.batchTest()) with a mocked #Service that doesn't have any #Retryable annotation and verify works fine.
What am I doing wrong?
In my pom.xml I have the following:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
...
Then all the related Java Classes
#SpringBootApplication
#EnableBatchProcessing
#Configuration
#EnableRetry
public class SampleBatchApplication {
#Autowired
private JobBuilderFactory jobs;
#Autowired
private StepBuilderFactory steps;
#Autowired
private MyRetryService myRetryService;
#Autowired
private MyServiceNoRetry myServiceNoRetry;
#Bean
protected Tasklet tasklet() {
return new Tasklet() {
#Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext context) {
myServiceNoRetry.process();
myRetryService.process();
return RepeatStatus.FINISHED;
}
};
}
#Bean
public Job job() throws Exception {
return this.jobs.get("job").start(step1()).build();
}
#Bean
protected Step step1() throws Exception {
return this.steps.get("step1").tasklet(tasklet()).build();
}
public static void main(String[] args) throws Exception {
// System.exit is common for Batch applications since the exit code can be used to
// drive a workflow
System.exit(SpringApplication
.exit(SpringApplication.run(SampleBatchApplication.class, args)));
}
#Bean
ResourcelessTransactionManager transactionManager() {
return new ResourcelessTransactionManager();
}
#Bean
public JobRepository getJobRepo() throws Exception {
return new MapJobRepositoryFactoryBean(transactionManager()).getObject();
}
}
#Service
public class MyRetryService {
public static final Logger LOG = LoggerFactory.getLogger(MyRetryService.class);
#Retryable(maxAttempts = 5, include = RuntimeException.class, backoff = #Backoff(delay = 100, multiplier = 2))
public boolean process() {
double random = Math.random();
LOG.info("Running process, random value {}", random);
if (random > 0.2d) {
throw new RuntimeException("Random fail time!");
}
return true;
}
}
#Service
public class MyServiceNoRetry {
public static final Logger LOG = LoggerFactory.getLogger(MyServiceNoRetry.class);
public boolean process() {
LOG.info("Running process that doesn't do retry");
return true;
}
}
#ActiveProfiles("Test")
#ContextConfiguration(classes = {SampleBatchApplication.class, MockBatchTestWithNoRetryWorking.MockedRetryService.class}, loader = AnnotationConfigContextLoader.class)
#RunWith(SpringRunner.class)
public class MockBatchTestWithNoRetryWorking {
#Autowired
MyServiceNoRetry service;
#Test
public void batchTest() {
service.process();
verify(service).process();
validateMockitoUsage();
}
public static class MockedRetryService {
#Bean
#Primary
public MyServiceNoRetry myService() {
return mock(MyServiceNoRetry.class);
}
}
}
#ActiveProfiles("Test")
#ContextConfiguration(classes = { SampleBatchApplication.class,
MockBatchTestWithRetryVerificationFailures.MockedRetryService.class },
loader = AnnotationConfigContextLoader.class)
#RunWith(SpringRunner.class)
public class MockBatchTestWithRetryVerificationFailures {
#Autowired
MyRetryService service;
#Test
public void batchTest() {
service.process();
verify(service).process();
validateMockitoUsage();
}
public static class MockedRetryService {
#Bean
#Primary
public MyRetryService myRetryService() {
return mock(MyRetryService.class);
}
}
}
EDIT: Updated question and code based on a sample project I put together to show the problem.
So after looking at a similar github issue for spring-boot
I found that there is an extra proxy getting in the way. I found a nasty hack by unwrapping the aop class by hand, makes verification work, ie:
#Test
public void batchTest() throws Exception {
service.process();
if (service instanceof Advised) {
service = (MyRetryService) ((Advised) service).getTargetSource().getTarget();
}
verify(service).process();
validateMockitoUsage();
}
Hopefully, this can be fixed similar to the above github issue. I'll raise an issue and see how far I get.
EDIT: Raised the github issue
After I've seen #Joel Pearsons answer, and especially the linked GitHub issue, I worked around this by temporarily using a static helper method that unwraps and verifies:
public static <T> T unwrapAndVerify(T mock, VerificationMode mode) {
return ((T) Mockito.verify(AopTestUtils.getTargetObject(mock), mode));
}
With this method the only difference in the test cases is the verification call. There is no overhead other than this:
unwrapAndVerify(service, times(2)).process();
instead of
verify(service, times(2)).process();
Actually, it was even possible to name the helper method like the actual Mockito method, so that you only need to replace the import, but I didn't like the subsequent confusion.
However, unwrapping shouldn't be required if #MockBean is used instead of mock() to create the mocked bean. Spring Boot 1.4 supports this annotation.

Resources