如何用Mockito模拟带Specification的Spring Data JPA Repository
解决Mockito模拟Specification参数不匹配的问题
问题核心是Mockito默认以对象引用相等匹配参数,测试中手动构建的Specification实例和Service运行时生成的是两个不同对象,导致匹配失败。以下是三种实用解决办法:
方法1:使用参数匹配器灵活匹配
如果无需校验Specification的具体逻辑,仅需让findAll返回预设结果,直接用any()匹配任意Specification参数:
when(userRepository.findAll(any(Specification.class))).thenReturn(userList);
若需要验证Specification的逻辑是否正确(比如是否包含预期的查询条件),用argThat自定义匹配规则:
when(userRepository.findAll(argThat(spec -> { // 模拟JPA查询上下文,验证Specification的逻辑 Root<User> root = mock(Root.class); CriteriaQuery<?> query = mock(CriteriaQuery.class); CriteriaBuilder cb = mock(CriteriaBuilder.class); // 根据你的UserSpecification实现,补充必要的Mock逻辑 when(root.get("id")).thenReturn(mock(Path.class)); when(cb.equal(any(Path.class), eq(1L))).thenReturn(mock(Predicate.class)); // 其他查询条件的Mock... Predicate predicate = spec.toPredicate(root, query, cb); // 根据需求判断predicate是否符合预期,此处示例直接返回true表示匹配 return true; }))).thenReturn(userList);
方法2:给UserSpecification实现equals和hashCode
让UserSpecification基于内部查询条件(userId、name、startDate、endDate)判断相等,而非默认的引用相等。这样只要条件一致,两个不同实例也会被判定为相等:
public class UserSpecification { private String userId; private String name; private LocalDate startDate; private LocalDate endDate; // 省略原有查询条件构建方法... @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserSpecification that = (UserSpecification) o; return Objects.equals(userId, that.userId) && Objects.equals(name, that.name) && Objects.equals(startDate, that.startDate) && Objects.equals(endDate, that.endDate); } @Override public int hashCode() { return Objects.hash(userId, name, startDate, endDate); } }
注意:如果是链式组合的Specification(如where().and().and()),需确保组合后的Specification也能正确判断相等,可改用单个包含所有条件的Specification类,或对组合逻辑做适配。
方法3:Mock UserSpecification实例
在测试中Mock Service依赖的UserSpecification,让它返回预设的Specification片段,确保Service构建的query与测试中的实例一致:
// 测试类中Mock依赖 @Mock private UserSpecification userSpecification; @Mock private UserRepository userRepository; @InjectMocks private UserServiceImpl userService; @Test void testFindUsers() { // 预设各个条件方法返回的Specification片段 Specification<User> userIdSpec = mock(Specification.class); Specification<User> nameSpec = mock(Specification.class); Specification<User> finalQuery = Specification.where(userIdSpec) .and(nameSpec) .and(mock(Specification.class)) .and(mock(Specification.class)); // Mock链式调用逻辑 when(userSpecification.userIdEqual("1")).thenReturn(userIdSpec); when(userSpecification.nameContains("john")).thenReturn(nameSpec); when(userIdSpec.and(any(Specification.class))).thenReturn(nameSpec); when(nameSpec.and(any(Specification.class))).thenReturn(finalQuery); // 其他and方法的Mock... // 匹配最终的query实例 when(userRepository.findAll(finalQuery)).thenReturn(userList); // 调用Service方法并验证结果 UserQueryTarget target = new UserQueryTarget(); target.setUserId("1"); target.setName("john"); // 设置其他查询参数... List<User> result = userService.findUsers(target); assertEquals(userList, result); }
内容的提问来源于stack exchange,提问作者harunaga
相关产品推荐
相关产品推荐

