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

Mockito测试中Mock Predicate执行返回空指针的问题排查

Mockito单元测试中Predicate执行时空指针问题排查与解决

问题根源

  1. 链式调用返回null导致NPE:你Mock的CriteriaQuery<Company>对象,其select()和where()方法未设置返回值,Mockito默认返回null。被测方法中使用query.select(root).where(predicate)链式调用,select()返回null后,调用where()直接触发空指针异常。
  2. 匹配器精度不足:测试中criteriaBuilder.and(any())的匹配器范围过宽,虽然不是直接引发NPE的原因,但可能导致Stub逻辑不符合预期。

修复方案

  1. 为CriteriaQuery的链式方法设置Stub,让它们返回自身(符合JPA Criteria API的链式设计);
  2. 优化匹配器精度,确保Stub逻辑精准匹配被测方法的调用;
  3. 补充必要的verify校验,确保调用流程符合预期。

修复后的测试代码

public class CompanyRepositoryTest {

    @Mock
    private EntityManager entityManager;

    @Mock
    private CriteriaBuilder criteriaBuilder;

    @Mock
    private CriteriaQuery<Company> criteriaQuery;

    @Mock
    private Root<Company> root;

    @Mock
    private TypedQuery<Company> typedQuery;
    
    @Mock
    private Path<Object> path;

    @InjectMocks
    private CompanyRepositoryImpl companyRepository;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
        // 配置CriteriaQuery的链式方法返回自身,避免链式调用返回null
        when(criteriaQuery.select(any(Root.class))).thenReturn(criteriaQuery);
        when(criteriaQuery.where(any(Predicate.class))).thenReturn(criteriaQuery);
    }

    @Test
    @DisplayName("Should find Company by CNPJ and return success")
    void findByCnpjSuccess() {
        String cnpj = "123456789";

        Company company = new Company();
        company.setCnpj(cnpj);

        List<Company> companies = new ArrayList<>();
        companies.add(company);

        when(entityManager.getCriteriaBuilder()).thenReturn(criteriaBuilder);
        when(criteriaBuilder.createQuery(Company.class)).thenReturn(criteriaQuery);
        when(criteriaQuery.from(Company.class)).thenReturn(root);
        
        when(root.get("cnpj")).thenReturn(path);
        
        Predicate predicate = mock(Predicate.class);
        // 使用精确匹配器,仅匹配Predicate类型参数
        when(criteriaBuilder.and(any(Predicate.class))).thenReturn(predicate);
        when(criteriaBuilder.equal(path, cnpj)).thenReturn(predicate);
        
        when(entityManager.createQuery(criteriaQuery)).thenReturn(typedQuery);
        when(typedQuery.getResultList()).thenReturn(companies);

        List<Company> result = companyRepository.findByCnpj(cnpj);

        assertEquals(1, result.size());
        assertEquals(cnpj, result.get(0).getCnpj());

        // 补充校验链式调用的方法
        verify(entityManager).getCriteriaBuilder();
        verify(criteriaBuilder).createQuery(Company.class);
        verify(criteriaQuery).from(Company.class);
        verify(criteriaBuilder).equal(root.get("cnpj"), cnpj);
        verify(criteriaBuilder).and(predicate);
        verify(criteriaQuery).select(root);
        verify(criteriaQuery).where(predicate);
        verify(entityManager).createQuery(criteriaQuery);
        verify(typedQuery).getResultList();
    }

}

关键修复说明

  • 在setUp()中配置criteriaQuery.select()和where()返回自身,保证链式调用不会因为返回null中断;
  • 将any()替换为any(Predicate.class),确保criteriaBuilder.and()的Stub仅匹配预期的参数类型;
  • 新增对select()、where()、and()的verify校验,确认被测方法的执行流程完全符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 20:00:29