如何测试Spring JPA的CriteriaBuilder.isFalse()方法?
问题描述
我有一个自定义方法,可从自定义对象创建Spring JPA的Specification,用于通过CriteriaBuilder.isFalse()方法检查属性是否为false。我使用Mockito编写了如下单元测试:
void unitTestIsNotDeleted() { Root root = mock(Root.class); CriteriaBuilder cb = mock(CriteriaBuilder.class); Predicate predicate = mock(Predicate.class); Path<Object> path = mock(Path.class); when(root.get("deleted")).thenReturn(path); when(cb.isFalse(path)).thenReturn(predicate); Specification<MyObj> specification = CustomSpecifications.isNotDeleted(); Assertions.assertNotNull(specification); Predicate result = specification.toPredicate(root, query, cb); Assertions.assertNotNull(result); Assertions.assertEquals(predicate, result); }
运行测试时出现如下异常:
java: incompatible types: jakarta.persistence.criteria.Path<java.lang.Object> cannot be converted to jakarta.persistence.criteria.Expression<java.lang.Boolean>
我曾用类似方式测试CriteriaBuilder.equals()未出现该异常。尝试通过中间Path<Boolean>转换Path<Object>,但因Mock误用出现PotentialStubbingProblem;使用Mockito.lenient()后测试可运行但结果失败,求解决办法。
解决办法
1. 直接匹配正确的泛型类型
CriteriaBuilder.isFalse()的参数要求是Expression<Boolean>,而Path<Boolean>是它的子类型,所以直接mockPath<Boolean>而非Path<Object>,从根源避免类型转换问题:
void unitTestIsNotDeleted() { Root<MyObj> root = mock(Root.class); CriteriaBuilder cb = mock(CriteriaBuilder.class); Predicate predicate = mock(Predicate.class); // 直接创建Path<Boolean>的mock对象 Path<Boolean> path = mock(Path.class); when(root.get("deleted")).thenReturn(path); // 此时参数类型完全匹配,不会触发类型转换异常 when(cb.isFalse(path)).thenReturn(predicate); Specification<MyObj> specification = CustomSpecifications.isNotDeleted(); Assertions.assertNotNull(specification); // 测试中query参数未被使用,可传null Predicate result = specification.toPredicate(root, null, cb); Assertions.assertNotNull(result); Assertions.assertEquals(predicate, result); }
2. 解决PotentialStubbingProblem
这个异常通常是因为mock对象的方法调用和你预先stub的逻辑不匹配,解决思路:
- 确保
root.get("deleted")的调用参数和stub时完全一致(比如字符串"deleted"不能写错) - 不要对同一个方法调用重复stub,避免逻辑冲突
- 如果确实需要宽松的stub规则,使用
lenient()时要保证stub的逻辑和实际调用一致:
lenient().when(root.get("deleted")).thenReturn(path);
3. 验证Specification的实际逻辑
确保你的CustomSpecifications.isNotDeleted()逻辑正确,比如:
public static Specification<MyObj> isNotDeleted() { return (root, query, cb) -> cb.isFalse(root.get("deleted")); }
只有实际逻辑和测试中的stub对应上,才能保证断言结果匹配。
内容的提问来源于stack exchange,提问作者bogu399

