3

I try to test a method named as loadData defined in MainController which returns result as a string. Despite that this method actually returns data when the web app runs on servlet container (or when I debug the code), no data returns when I invoke it from a test class based on JUnit 5 with Mockito.

Here is my configuration:

@ContextConfiguration(classes = {WebAppInitializer.class, AppConfig.class, WebConfig.class})
@Transactional
@WebAppConfiguration
public class TestMainController {

    @InjectMocks
    private MainController mainController;

    private MockMvc mockMvc;

    @BeforeEach
    public void init() {
        mockMvc = MockMvcBuilders.standaloneSetup(this.mainController).build();
    }

    @Test
    public void testLoadData() throws Exception {
        MvcResult mvcResult = mockMvc
                .perform(MockMvcRequestBuilders.get("/loadData.ajax"))
                .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();

        Assertions.assertNotNull(mvcResult.getResponse().getContentAsString(), "response should not be null");
    }

}

The test fails due to java.lang.NullPointerException as the this.mainController is null.

Environment Details:

Spring version: 5.0.3
JUnit version: 5.0.3
mockito version: 1.9.5
hamcrest version: 1.3
json-path-assert version: 2.2.0

Edit: Here is the loadData method of MainController:

@RequestMapping(value = "/loadData.ajax", method = RequestMethod.GET)
public String loadData(HttpServletRequest request, HttpServletResponse response) {
    List list = mainService.loadData(); // starts a transaction and invokes the loadData method of mainDAO repository which basically loads data from the database
    return JSONArray.fromObject(list).toString();
}
talha06
  • 6,206
  • 21
  • 92
  • 147
  • 1
    You don't call the controller's method directly with MockHttp. You simulate the use of an URL : https://docs.spring.io/spring-security/site/docs/current/reference/html/test-mockmvc.html – Guillaume F. Feb 11 '18 at 06:50
  • Let's say the URL I want to test is `loadData.ajax`. Could you please guide me to setup the test environment? Tried the following through the guide you have shared, but no luck: `mockMvc.perform(get("/loadData.ajax"));` – talha06 Feb 11 '18 at 08:44
  • Add your `MainController`. What is strange on your test is that you have both a web and regular configuration, shouldn't you have a single one? – M. Deinum Feb 11 '18 at 10:33
  • @M.Deinum `AppConfig` is for creating `session factory` and `transaction manager` beans. `WebConfig` implements `WebMvcConfigurer` and defines all the other required beans including `view revolvers`, `interceptors`, `tasks`, etc. – talha06 Feb 11 '18 at 12:37
  • You don't need to `Autowire` the controller You create an instance of `MockMvc` as the following, If the Controller's constructor takes any parameter then you pass them to the controller. `MockMvcBuilders.standaloneSetup(new YourController()).build();` – Ali.Wassouf Feb 11 '18 at 14:11
  • @Ali.Wassouf tried that not worked `public class TestMainController { @InjectMocks private MainController mainController; @Mock private MainService mainService; private MockMvc mockMvc; @BeforeEach public void setup() { // Process mock annotations MockitoAnnotations.initMocks(this); this.mockMvc = MockMvcBuilders.standaloneSetup(new MainController()).build(); } @Test public void testLoadData() throws Exception { this.mockMvc.perform(get("/loadData.ajax")).andExpect(status().isOk()); } }` – talha06 Feb 11 '18 at 15:20
  • As stated please add your `MainController` so that we can see what is happening in there. – M. Deinum Feb 12 '18 at 07:42
  • 2
    It is **not** possible to declare `@SpringJUnitWebConfig` _and_ `@SpringJUnitConfig` on the same test class. Spring will only honor **one** of them, namely the first one it finds. – Sam Brannen Feb 12 '18 at 15:37
  • @M.Deinum Thanks for your care; the related method definition is added into the post. – talha06 Feb 13 '18 at 14:30
  • Your test is wrong. You create a mocked controller but don't mock the dependencies or define behavior. You also load the context but don't actually use it. – M. Deinum Feb 13 '18 at 14:57
  • So, could you please provide the right one? @M.Deinum – talha06 Feb 13 '18 at 14:59

1 Answers1

5

You can call controller method directly, just like we do for service method, but this is not recommended. Using MockMvc, you check for header and request param mapping are proper. Plus, you also check for end point mapping is correct. Plus the Request METHOD is also correct. All these you cannot test if you test your code by directly calling the controller method.

One thing you can try is, instead of creating new object inside the standalone context, use the Mock. i.e

mockMvc = MockMvcBuilders.standaloneSetup(this. mainController).build();

And while calling, do this

MvcResult mvcResult = mockMvc
    .perform(MockMvcRequestBuilders.get("/loadData.ajax"))
    .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();

Assert , what you would like to

Assert.assertEquals("response does not match", mvcResult.getResponse().getContentAsString(),
    "some expected response");

You are getting null or 400 or 404 http status ?

If you are getting 400, then please check the header and req. param if any are proper. If you are getting 404 then please check the URL path. /loadData.ajax , assuming this is your request mapping path in controller method.

lrathod
  • 1,094
  • 1
  • 9
  • 17
  • Getting `NullPointerException` as the `this.mainController` is found as null on the line when the `mockMvc` is initialized. – talha06 Feb 13 '18 at 14:50
  • Please add **@RunWith(MockitoJUnitRunner.class)** at class level. Something like this. @RunWith(MockitoJUnitRunner.class) public class TestMainController { then you variable mainController will be initialize – lrathod Feb 13 '18 at 15:47
  • Could not find the `RunWith` class which is a part of `JUnit 4`. – talha06 Feb 13 '18 at 15:54
  • import org.junit.runner.RunWith; i see this under junit:4.12 version. and in build.gradle testCompile("junit:junit:4.12") – lrathod Feb 13 '18 at 17:22
  • Both JUnit 4 & 5 on the classpath? Does not sound good, as the annotations are defined on the different packages. Getting the exception `No tests found in TestMainController Haven't you forgot @Test annotation?` – talha06 Feb 13 '18 at 17:34
  • Really sorry. Forgot you are using Junit5. I have not using JUnit5 with Mockito yet, but a quick search resulted me with this **"In JUnit 5, the RunWith annotation has been replaced by the more powerful ExtendWith annotation."** may be this will help you http://www.baeldung.com/junit-5-runwith – lrathod Feb 13 '18 at 17:51
  • no problem but actually `MockitoJUnitRunner` is not compatible with `ExtendWith` annotation. – talha06 Feb 13 '18 at 20:27