Home > database >  NullPointerException MockMvc with ExtendWith (test Junit Springboot)
NullPointerException MockMvc with ExtendWith (test Junit Springboot)

Time:12-01

I have a problem doing tests Mocking with Junit. It turns out that if I put the @SpringBootTest annotation it works perfectly, on the other hand if I remove it and leave it only with @ExtendWith(MockitoExtension.class) so that the tests load faster, it gives me the following error and it doesn't work. I don't know if I need to put another annotation, or what I should do.

java.lang.NullPointerException: Cannot invoke "org.springframework.test.web.servlet.MockMvc.perform(org.springframework.test.web.servlet.RequestBuilder)" because "this.mockMvc" is null

`

import com.google.gson.Gson;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;

import javax.ws.rs.core.MediaType;
import java.util.HashMap;
import java.util.Map;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;


//@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
@ExtendWith(MockitoExtension.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class TestController {

    @Autowired
    MockMvc mockMvc;

    Gson gson = new Gson();

    @Test
    public void addPer() throws Exception {
        PostPer postPerDto = PostPer.builder()
                .nombre("nombre")
                .detalles("detalles")
                .precio(50.0f)
                .build();
        mockMvc.perform(post("/per/addPer")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(gson.toJson(postPerDto)))
                .andDo(print())
                .andExpect(status().isCreated());
    }

 }

`

I have tried different solutions such as using RunWith that I have seen around here, and nothing. I did that too Spring Boot JUnit 5 test with @ExtendWith(MockitoExtension.class) not working - mocks are null, MockitoJUnitRunner is working

CodePudding user response:

Your test is calling an endpoint so you need at least the controller and web layer to be there. You cannot do this easily with only a unit test.

Removing @SpringBootTest does not just 'make the test load faster'. It also omits firing up the application context and web layer which you need for your test.

The test application context is cached by Spring, allowing multiple test methods to run faster than each one individually. The cache is controlled via the DirtiesContext. Currently it is set to BEFORE_EACH_TEST_METHOD, maybe you could try BEFORE_CLASS so it cleans up the cache once before the test class instead of before each test method.

Also you can use @WebMvcTest instead of @SpringBootTest. This intializes only the spring web layer instead of the complete application context. And you can narrow that down to a specific controller by providing its class.

So you can optimize your test a bit like this:

@WebMvcTest(Controller.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
public class TestController {
    @Autowired
    private MockMvc mockMvc;
    private Gson gson = new Gson();

See this tutorial to gain understanding of the different Spring options to test the web layer

EDIT: As a simple unit test:

@ExtendWith(MockitoExtension.class)
public class TestController {

    @InjectMocks
    private Controller controller;

    @Test
    public void addPer() {
        PostPer postPerDto = PostPer.builder()
            .nombre("nombre")
            .detalles("detalles")
            .precio(50.0f)
            .build();

        PostPerResponse response = controller.postPer(postPerDto);

        assertEquals(200, response.getStatus());
    }

}
  • Related