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

Spring Boot JUnit测试中Repository空指针异常求助

解决Spring Boot JUnit测试中Repository返回空值触发NPE的问题

问题分析

你的测试代码存在几个关键问题:

  1. 手动设置自增ID:Employee主键使用GenerationType.IDENTITY自增策略,但测试中手动指定id=1L,干扰了数据库自增逻辑,save操作后原对象ID不会被数据库更新,直接断言原对象ID会不符合预期。
  2. 测试分层混淆:类名是EmployeeControllerTest,但你用了@DataJpaTest(用于测试Repository层),未测试Controller接口逻辑,反而直接操作Repository。
  3. 数据库配置问题:@DataJpaTest默认使用嵌入式内存数据库(如H2),不会连接你配置的PostgreSQL,若期望用真实库测试需额外配置。
  4. Mockito未正确使用:你提到用Mockito,但当前测试完全依赖真实Repository,未用到Mock逻辑。

解决方案

方案1:修正Repository层测试(用@DataJpaTest)

如果目标是测试Repository的save方法,修改测试代码如下:

@DataJpaTest
// 若要连接真实PostgreSQL,添加以下注解,否则默认用H2内存库
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class EmployeeRepositoryTest { // 类名改为对应Repository测试更清晰
    @Autowired
    private EmployeeRepository employeeRepository;

    @Test
    public void addEmployeeTest() {
        // 不要手动设置id,交给数据库自增生成
        Employee employee = Employee.builder()
                        .firstName("abc")
                        .lastName("def")
                        .email("abc@xyz.com")
                        .build();

        // 接收save方法返回的实体,该实体包含数据库生成的ID
        Employee savedEmployee = employeeRepository.save(employee);

        assertThat(savedEmployee.getId()).isGreaterThan(0L);
    }
}

方案2:正确测试Controller层(用Mockito+@WebMvcTest)

如果目标是测试Controller的createEmployee接口,这才是Mockito的典型使用场景,代码如下:

@WebMvcTest(EmployeeController.class)
public class EmployeeControllerTest {
    @Autowired
    private MockMvc mockMvc;

    // Mock依赖的EmployeeRepository,避免依赖真实数据库
    @MockBean
    private EmployeeRepository employeeRepository;

    @Test
    public void createEmployeeTest() throws Exception {
        // 构造请求参数
        Employee requestEmployee = Employee.builder()
                        .firstName("abc")
                        .lastName("def")
                        .email("abc@xyz.com")
                        .build();
        // 构造模拟的返回结果
        Employee mockSavedEmployee = Employee.builder()
                        .id(1L)
                        .firstName("abc")
                        .lastName("def")
                        .email("abc@xyz.com")
                        .build();

        // 指定当调用Repository的save方法时,返回模拟的实体
        when(employeeRepository.save(any(Employee.class))).thenReturn(mockSavedEmployee);

        // 发送POST请求测试接口
        mockMvc.perform(post("/api/v1/employees")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(new ObjectMapper().writeValueAsString(requestEmployee)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").value(1L))
                .andExpect(jsonPath("$.firstName").value("abc"))
                .andExpect(jsonPath("$.lastName").value("def"))
                .andExpect(jsonPath("$.email").value("abc@xyz.com"));
    }
}

关键说明

  • 自增ID处理:IDENTITY策略下,数据库会自动生成ID,save方法返回的实体才包含最新的ID,原对象的ID不会被自动更新。
  • 测试分层:不同层级的测试用对应注解:@DataJpaTest测试Repository层,@WebMvcTest测试Controller层,@SpringBootTest用于集成测试。
  • Mockito使用:测试Controller时,通过@MockBean模拟Repository,避免依赖真实数据库,专注测试Controller的逻辑是否正确调用依赖并返回结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 21:20:29