Search code examples
spring-bootjunitmockingmockitospring-boot-test

Null is passed to autowired service even after mocking - SpringBootTest


I wanted to do integration testing of my API.

@RestController
@RequestMapping("/api/v1")
public class TestController {

    @Autowired
    TestService testService;

    @RequestMapping("/welcome")
    public String welcomeMessage(@RequestParam("name") String name) {
        return testService.welcomeMessage(name);
    }
}

Below are the service interface and its implementation:

public interface TestService {
    public String welcomeMessage(String name);
}

public class TestServiceImpl implements TestService{
    @Autowired
    TestRepository repo;

    @Override
    public String welcomeMessage(String name) {
        repo.save(new StringEntity(name));
        return "Hello "+name;
    }
}

Below is the Test Case:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MockitoTestingApplicationTests {

    @Autowired
    MockMvc mvc;

    @MockBean
    TestService testService;

    @MockBean
    TestController testController;

    @Test
    public void contextLoads() throws Exception {
        Mockito.when(testController.welcomeMessage(ArgumentMatchers.anyString())).thenCallRealMethod();
        Mockito.when(testService.welcomeMessage(ArgumentMatchers.anyString())).thenCallRealMethod();

        mvc.perform(get("/api/v1/welcome").param("name", "dude")).andExpect(status().isOk());
    }

}

I have a few questions.

  1. when I'm executing the above code it is throwing an error saying cannot call real method on abstract methods. And When I'm mocking the TestServiceImpl, It is throwing NullPointerException in the Controller because the TestService is null. How should I fix that?

  2. How should I mock the repository layer when we are using MongoDB. when I try to Mock MongoTemplate, It is throwing an error saying MongoConvertor must not be null

  3. Is this the right way to write test cases. can we have code coverage without using thenCallRealMethod()?

Please suggest me how to proceed. Thanks in advance.


Solution

  • Make sure you have an implementation of the service i.e. TestServiceImpl annotated with @Service (or @Component if it is not strictly a service) and use spying instead of mocking:

    @SpyBean
    TestService testService;
    

    Spying by default call real methods so you have to mock these that implementation you do not want to call.

    Regarding repositories, you should mock the components annotated with @Repository, not the actual SessionFactory / Template etc. that are used within.