Spring Boot中使用Specification编写JUnit测试遇空指针及参数匹配问题
问题解决思路与方案
核心问题分析
你的两个报错本质是同一个原因:Specification是Lambda表达式实现的,每次调用AdminUserSpecifications.hasOrganizationId()都会生成全新的实例。测试代码中手动创建的specification对象,和业务方法里生成的specification不是同一个实例,导致Mockito的参数匹配失败:
- 初始测试时,匹配失败导致
findAll()返回Mockito默认的null,触发空指针异常; - 改用
any(Specification.class)后,若Pageable的匹配存在问题(比如多次调用toPageable()生成不同实例),也会出现参数不匹配。
具体解决方案
方案1:简化参数匹配(快速解决)
直接用any(Specification.class)匹配Specification参数,同时确保Pageable参数匹配(同一AdminUserListRequest对象调用toPageable()返回的实例通常是相等的,可用eq()匹配)。
修改测试代码的When和Then部分:
// When Mockito.when(identity.getUserType()).thenReturn(userType); Mockito.when(identity.getOrganizationId()).thenReturn(mockAdminUserEntity.getOrganizationId()); // 改用any匹配Specification,eq匹配Pageable Mockito.when(adminUserRepository.findAll( Mockito.any(Specification.class), Mockito.eq(mockAdminUserListRequest.toPageable()) )).thenReturn(mockPageAdminUserEntities); Page<AdminUser> pageAdminUsers = adminUserService.getAdminUsers(mockAdminUserListRequest); // Then PageBuilder.assertEquals(mockPageAdminUsers, pageAdminUsers); // 验证时使用相同的匹配器 Mockito.verify(adminUserRepository, Mockito.times(1)) .findAll(Mockito.any(Specification.class), Mockito.eq(mockAdminUserListRequest.toPageable()));
方案2:自定义匹配器(严格验证逻辑)
如果需要验证Specification的逻辑是否正确(比如是否使用了预期的organizationId),可以自定义ArgumentMatcher来检查:
- 编写匹配器类:
class AdminUserSpecificationMatcher implements ArgumentMatcher<Specification<AdminUserEntity>> { private final String expectedOrgId; public AdminUserSpecificationMatcher(String expectedOrgId) { this.expectedOrgId = expectedOrgId; } @Override public boolean matches(Specification<AdminUserEntity> spec) { // 模拟JPA查询所需的对象 Root<AdminUserEntity> root = Mockito.mock(Root.class); CriteriaQuery<?> query = Mockito.mock(CriteriaQuery.class); CriteriaBuilder cb = Mockito.mock(CriteriaBuilder.class); Path<Object> orgIdPath = Mockito.mock(Path.class); // 模拟路径获取 Mockito.when(root.get("organizationId")).thenReturn(orgIdPath); // 模拟equal条件构建 Mockito.when(cb.equal(orgIdPath, expectedOrgId)).thenReturn(Mockito.mock(Predicate.class)); // 执行Specification的逻辑,验证是否生成有效Predicate Predicate predicate = spec.toPredicate(root, query, cb); return predicate != null; } }
- 在测试中使用该匹配器:
// When Mockito.when(adminUserRepository.findAll( Mockito.argThat(new AdminUserSpecificationMatcher(mockAdminUserEntity.getOrganizationId())), Mockito.eq(mockAdminUserListRequest.toPageable()) )).thenReturn(mockPageAdminUserEntities); // Then Mockito.verify(adminUserRepository, Mockito.times(1)) .findAll( Mockito.argThat(new AdminUserSpecificationMatcher(mockAdminUserEntity.getOrganizationId())), Mockito.eq(mockAdminUserListRequest.toPageable()) );
额外注意事项
- 确保
AdminUserListRequest.toPageable()方法返回的Pageable实例重写了equals和hashCode,否则eq()匹配会失败; - 如果业务中
organizationId可能为null,需要补充空值场景测试,验证SearchSpecificationBuilder.eq()返回null时的逻辑。
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu
相关产品推荐
相关产品推荐

