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

Spring Boot API测试调用Repository抛出NullPointerException问题求助

问题原因与修复方案

核心问题

你所说的报错指向companiaRepository.save(compania)属于行号误判,实际NPE触发点在Controller的Collections.singletonMap调用处,和错误栈信息一致,核心问题有三个:

  • 注解逻辑冲突:你同时使用了@SpringBootTest(Spring Boot集成测试注解,会启动完整Spring上下文)和Mockito的@Mock/@InjectMocks(单元测试模拟依赖注解),两种测试模式混用导致逻辑混乱。
  • Mock对象无方法打桩:你用@Mock生成的CompaniaRepository是虚拟对象,默认所有方法返回值为null,且不会真的和你配置的H2数据库交互:
    1. setUp方法里调用companiaRepository.save(compania)不会将数据存入H2库,只是调用了Mock对象的空方法
    2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 15:45:04