Spring Boot API测试调用Repository抛出NullPointerException问题求助
问题原因与修复方案
核心问题
你所说的报错指向companiaRepository.save(compania)属于行号误判,实际NPE触发点在Controller的Collections.singletonMap调用处,和错误栈信息一致,核心问题有三个:
- 注解逻辑冲突:你同时使用了
@SpringBootTest(Spring Boot集成测试注解,会启动完整Spring上下文)和Mockito的@Mock/@InjectMocks(单元测试模拟依赖注解),两种测试模式混用导致逻辑混乱。 - Mock对象无方法打桩:你用
@Mock生成的CompaniaRepository是虚拟对象,默认所有方法返回值为null,且不会真的和你配置的H2数据库交互:setUp方法里调用companiaRepository.save(compania)不会将数据存入H2库,只是调用了Mock对象的空方法- Service层调用
companiaRepository.findAll()会返回null,Controller层调用Collections.singletonMap("result", null)时会触发空指针异常(Collections.singletonMap不允许value为null)
- 依赖注入逻辑错误:你手动实例化了
ControllerAPI controllerAPI = new ControllerAPI(companiaService);,如果是集成测试场景下应该从Spring容器注入Bean,而不是手动创建。
修复方案
方案1:集成测试(使用真实H2数据库)
删掉所有Mockito相关注解,直接从Spring容器注入依赖,测试代码修改如下:
@RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureTestDatabase class ControllerAPITest { // 注入真实的Repository Bean,会自动连接配置的H2数据库 @Autowired CompaniaRepository companiaRepository; @Autowired DefaultGroupRepository defaultGroupRepository; @Autowired OfficeRepository officeRepository; // 注入真实的Service和Controller @Autowired CompaniaServiceImpl companiaService; @Autowired ControllerAPI controllerAPI; @BeforeEach void setUp() { Compania compania = new Compania(); compania.setName("companiaTest"); compania.setDominio("dominioTest"); compania.setAltas("altasTest"); compania.setBajas("bajasTest"); compania.setDefault_group(null); compania.setOffice(null); companiaRepository.save(compania); } @AfterEach void tearDown() { companiaRepository.deleteAll(); } @Test void getByField() { } @Test void getCompanias() { System.out.println(controllerAPI.GetCompanias()); } }
方案2:单元测试(不需要启动Spring上下文和H2数据库)
删掉@SpringBootTest和@AutoConfigureTestDatabase注解,改用Mockito的测试注解,给Mock的Repository方法提前打桩指定返回值:
// 用Mockito测试扩展,不需要启动Spring上下文 @ExtendWith(MockitoExtension.class) class ControllerAPITest { @Mock CompaniaRepository companiaRepository; @Mock DefaultGroupRepository defaultGroupRepository; @Mock OfficeRepository officeRepository; @InjectMocks CompaniaServiceImpl companiaService; ControllerAPI controllerAPI; @BeforeEach void setUp() { controllerAPI = new ControllerAPI(companiaService); // 构造测试数据 Compania compania = new Compania(); compania.setName("companiaTest"); compania.setDominio("dominioTest"); compania.setAltas("altasTest"); compania.setBajas("bajasTest"); compania.setDefault_group(null); compania.setOffice(null); List<Compania> testList = Collections.singletonList(compania); // 给Mock的Repository方法打桩,指定调用findAll时返回测试数据 Mockito.when(companiaRepository.findAll()).thenReturn(testList); } @Test void getByField() { } @Test void getCompanias() { System.out.println(controllerAPI.GetCompanias()); } }
内容的提问来源于stack exchange,提问作者Varox
相关产品推荐
相关产品推荐

