Mockito测试中Mock Predicate执行返回空指针的问题排查
Mockito单元测试中Predicate执行时空指针问题排查与解决
问题根源
- 链式调用返回null导致NPE:你Mock的
CriteriaQuery<Company>对象,其select()和where()方法未设置返回值,Mockito默认返回null。被测方法中使用query.select(root).where(predicate)链式调用,select()返回null后,调用where()直接触发空指针异常。 - 匹配器精度不足:测试中
criteriaBuilder.and(any())的匹配器范围过宽,虽然不是直接引发NPE的原因,但可能导致Stub逻辑不符合预期。
修复方案
- 为
CriteriaQuery的链式方法设置Stub,让它们返回自身(符合JPA Criteria API的链式设计); - 优化匹配器精度,确保Stub逻辑精准匹配被测方法的调用;
- 补充必要的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
相关产品推荐
相关产品推荐

