the #Mock and #InjectMocks annotations don't work - spring-boot

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

Related

Unit Test Elasticsearch Repository Null Values

In my Test class, I am trying to send a request to an API and compare the results with the ones that are sent from Springboot using elastic search data.
I am able to send the request via WebClient. However, the Service and Repository objects are always null. Any help would be great. Thanks
#RunWith(SpringRunner.class)
class MyTest {
#Autowired
private ESService esService; //this is null
#Autowired
private OtherRepository otherRepo; //this is null
#Test
private void testFunction(){
TestObject Object = esService.doSomething();
double value = new TestObject(otherRepo).getValue();
}
}
#Service
public interface ESService{
TestObject doSomething();
}
#Service
public class ESServiceImpl implements ESService{
#Autowired
private ESRepository esRepository;
TestObject doSomething(){
return esRepository.doSomething();
}
}
#Repository
public interface ESRepository extends ElasticsearchRepository<QueryResponse, String>, ESRepositoryCustom {
}
pom.xml
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>

Why do I keep getting 404's when using #AutoConfigureMockMvc in SpringBoot test?

I'm trying to write an integration test using springrunner. I'm running it as a springboottest and using autoconfiguremockmvc. However, i keep getting 404s. It seems my controllers/endpoints aren't being loaded by the autoconfiguremockmvc. Does anyone know a solution of how to wire this up so that it will pick up my controllers?
I did add a basic controller into my test class and i was able to hit it successfully but I have been unable so far to hit the actual controller I want to use in my integration test.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { TestConfig.class })
#TestPropertySource(locations = "classpath:application-test.properties")
#AutoConfigureMockMvc
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void test() throws Exception {
MockHttpServletRequestBuilder request =
MockMvcRequestBuilders.post(URL).contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.content(json);
final ResponseEntity<String> response =
new ResponseEntity<>("works", HttpStatus.OK);
final ResultActions result = this.mockMvc.perform(request).andDo(print());
result.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
}
}
#TestConfiguration
#EnableAutoConfiguration
public class TestConfig {
}
You could use
#SpringBootTest(webEnvironment =
SpringBootTest.WebEnvironment.RANDOM_PORT)
and RestAssured library in combination to test any endpoint/api like below
// include dependency in pom
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>3.0.5</version>
<scope>test</scope>
</dependency>
//Test Class
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntTest{
//inject test local port
#LocalServerPort
private int port;
#Before
public void setUp() {
//assign port for reset assured
RestAssured.port = port;
}
#Test
public void test() {
given()
.get("/api")
.then()
.assertThat()
.statusCode(HttpStatus.OK.value())
.contentType(ContentType.JSON)
.body("name", is("test"));
}
}

thymeleaf test case spring boot

#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?

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.

How do I change this Spring Test Framework Test to work with a JSON return?

I am trying to change the following Spring Test Framework to test for a the following json return:
{"user":"John ","name":"JohnSmith"}
Here is my test code and I just dont know how to change it to check for JSON and the value in JSON. It will be great if someone can tell me what to change.. thanks
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebMVCConfig.class})
#WebAppConfiguration
public class TestHelloWorldWeb
{
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup()
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getFoo() throws Exception
{
this.mockMvc.perform(get("/ask")
.accept(MediaType.TEXT_HTML))
.andExpect(status().isOk())
.andExpect(view().name("helloworld"))
.andExpect(MockMvcResultMatchers.model().attribute("user", "JohnSmith"))
;
}
}
.andExpect(jsonPath("$.user").value("john"));
with dependency :
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>0.8.1</version>
</dependency>
and one static import like this
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
adds the static method...

Resources