You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java RestAPI测试报错:this.service为空无法调用PacienteService.findById

解决@WebMvcTest中Mock Service类为空的NullPointerException问题

测试Spring Boot编写的RestAPI时,使用@WebMvcTest测试PacienteController,启动测试后抛出空指针异常,错误信息如下:

java.lang.NullPointerException: Cannot invoke "com.tcc.tccbackend.services.PacienteService.findById(java.lang.Integer)" because "this.service" is null

测试代码

PacienteTest类:

@WebMvcTest(PacienteController.class)
public class PacienteTest extends BaseTest {

    @Mock
    private PacienteService service;

    @Autowired
    private MockMvc mockMvc;

    @BeforeEach
    public void setup() {
        RestAssuredMockMvc.mockMvc(mockMvc);
    }

    @Test
    @DisplayName("Retorna sucesso quando busca um paciente ")
    public void t4() {
        Mockito.when(service.findById(9999))
                .thenReturn(new Paciente(9999, "Gilberson", "gilber@gmail.com", "68211836104", "(67) 99625-5371", new Date(), List.of()));
        RestAssuredMockMvc
                .given()
                .header("Authorization", getJWT())
                .accept(ContentType.JSON)
                .when()
                .get("/pacientes/9999")
                .then().statusCode(200);
    }
}

BaseTest类:

public class BaseTest {
    public BaseTest(){
        baseURI = "http://localhost";
        port = 8080;
        basePath = "/api";
    }

    public static String getJWT() {
        return given()
                .body("{\n" + "\t\"email\": \"paula@gmail.com\",\n" + "\t\"senha\": \"senha\"\n" + "}")
                .contentType(ContentType.JSON)
                .when()
                .post("/user/login")
                .then()
                .extract()
                .path("token");
    }
}

环境版本

  • Java 17
  • RestAssured 5.3.0
  • SpringBoot 2.7.0
  • SpringBoot Starter Test 2.7.0
  • Junit 4.13.2

问题原因

@Mock注解仅由Mockito管理,不会自动注册到Spring容器中,而@WebMvcTest启动的测试上下文只会加载Controller及其依赖的Bean。此时Controller依赖的PacienteService没有被正确注入Mock实例,导致Controller中的service字段为空,调用方法时抛出NPE。

解决方案

方案1:使用@MockBean替代@Mock

@MockBean是Spring Boot提供的注解,会自动将Mock对象注册到Spring测试上下文,确保Controller能注入到这个Mock实例:

@WebMvcTest(PacienteController.class)
public class PacienteTest extends BaseTest {

    // 将@Mock替换为@MockBean
    @MockBean
    private PacienteService service;

    @Autowired
    private MockMvc mockMvc;

    @BeforeEach
    public void setup() {
        RestAssuredMockMvc.mockMvc(mockMvc);
    }

    // 测试方法不变
}

方案2:手动初始化Mockito注解(保留@Mock)

在@BeforeEach方法中调用MockitoAnnotations.openMocks(this),手动初始化所有标注@Mock的字段:

@WebMvcTest(PacienteController.class)
public class PacienteTest extends BaseTest {

    @Mock
    private PacienteService service;

    @Autowired
    private MockMvc mockMvc;

    @BeforeEach
    public void setup() {
        // 初始化Mock对象
        MockitoAnnotations.openMocks(this);
        RestAssuredMockMvc.mockMvc(mockMvc);
    }

    // 测试方法不变
}

额外注意事项

当前BaseTest中的getJWT()方法直接调用真实的登录接口,这不符合单元测试的隔离原则(@WebMvcTest仅启动Controller层,不会加载完整服务)。建议:

  • 使用@MockBean Mock用户认证相关的Service,让getJWT()返回固定的测试Token
  • 若使用Spring Security,可添加@WithMockUser注解跳过真实权限验证

内容的提问来源于stack exchange,提问作者Victor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 20:10:29