java 无法在Sping Boot 中使用Specification为Junit Test调用“org.springframework.data.domain.Page.getContent()”

f4t66c6m  于 2023-05-21  发布在  Java
关注(0)|答案(1)|浏览(141)

我尝试在Sping Boot 中使用规范而不是使用findAllByAnything。当我尝试使用Specification for JUnit实现一个测试方法时,我得到了如下所示的错误。
我该如何解决问题?
下面getAdminUsersWithAdmin中显示的这部分返回null

Page<AdminUserEntity> adminUserEntitiesByOrganization = adminUserRepository.findAll(specification, listRequest.toPageable());

下面是名为getAdminUsersWithAdmin的相关方法

private Page<AdminUser> getAdminUsersWithAdmin(AdminUserListRequest listRequest) {
        String organizationId = identity.getOrganizationId();
        Specification<AdminUserEntity> specification = Specification
                .where(AdminUserSpecifications.hasOrganizationId(organizationId));
        Page<AdminUserEntity> adminUserEntitiesByOrganization = adminUserRepository.findAll(specification, listRequest.toPageable());
        List<AdminUser> adminUsersByOrganization = adminEntityToAdminMapper.map(adminUserEntitiesByOrganization.getContent());
        return Page.of(adminUserEntitiesByOrganization, adminUsersByOrganization);
    }

下面是AdminUserSpecifications

public class AdminUserSpecifications {

    
    public static Specification<AdminUserEntity> hasOrganizationId(String organizationId) {
        return (root, query, criteriaBuilder) ->
                SearchSpecificationBuilder.eq(criteriaBuilder, root.get("organizationId"), organizationId);
    }
}

下面是SearchSpecificationBuilder中的eq方法

public static Predicate eq(CriteriaBuilder criteriaBuilder, Path<Object> path, Object value) {
        if (value == null) {
            return null;
        }
        return criteriaBuilder.equal(path, value);
    }

下面是测试方法

@Test
    void givenUserListRequest_whenAdminwithRoleIsAdmin_thenReturnAllAdminUsers() {
        // Given
        AdminUserListRequest mockAdminUserListRequest = new AdminUserListRequestBuilder().withValidValues().build();

        AdminUserEntity mockAdminUserEntity = new AdminUserEntityBuilder().withValidFields().build();

        List<AdminUserEntity> mockAdminUserEntities = Collections.singletonList(mockAdminUserEntity);
        Page<AdminUserEntity> mockPageAdminUserEntities = new PageImpl<>(mockAdminUserEntities);

        List<AdminUser> mockAdminUsers = ADMIN_ENTITY_TO_ADMIN_MAPPER.map(mockAdminUserEntities);
        Page<AdminUser> mockPageAdminUsers = Page.of(mockPageAdminUserEntities, mockAdminUsers);

        UserType userType = UserType.ADMIN;
        Specification<AdminUserEntity> specification = Specification.where(AdminUserSpecifications.hasOrganizationId(mockAdminUserEntity.getOrganizationId()));

        // When
        Mockito.when(identity.getUserType()).thenReturn(userType);
        Mockito.when(identity.getOrganizationId()).thenReturn(mockAdminUserEntity.getOrganizationId());
        Mockito.when(adminUserRepository.findAll(specification, mockAdminUserListRequest.toPageable()))
                .thenReturn(mockPageAdminUserEntities);

        Page<AdminUser> pageAdminUsers = adminUserService.getAdminUsers(mockAdminUserListRequest);

        // Then
        PageBuilder.assertEquals(mockPageAdminUsers, pageAdminUsers);

        Mockito.verify(adminUserRepository, Mockito.times(1))
                .findAll(specification, mockAdminUserListRequest.toPageable());
    }

下面是运行givenUserListRequest_whenAdminwithRoleIsAdmin_thenReturnAllAdminUsers方法时显示的错误。

java.lang.NullPointerException: Cannot invoke "org.springframework.data.domain.Page.getContent()" because "adminUserEntitiesByOrganization" is null

编辑
我修改了如下所示的这一行

Mockito.when(adminUserRepository.findAll(Mockito.any(Specification.class), Mockito.eq(mockAdminUserListRequest.toPageable()))) .thenReturn(mockPageAdminUserEntities);

我得到这个问题如下所示

Argument(s) are different! Wanted:
adminUserRepository.findAll(
    com.admin_user.repository.specification.AdminUserSpecifications$$Lambda$421/0x000000080121b220@7f02251,
    Page request [number: 0, size 10, sort: UNSORTED]
);

adminUserRepository.findAll(
    com.admin_user.repository.specification.AdminUserSpecifications$$Lambda$421/0x000000080121b220@dffa30b,
    Page request [number: 0, size 10, sort: UNSORTED]
);
u91tlkcl

u91tlkcl1#

问题就出在这一行:

Mockito.when(adminUserRepository.findAll(specification, mockAdminUserListRequest.toPageable()))
                .thenReturn(mockPageAdminUserEntities);

你的规范是一个lambda -Specification.where返回传入的规范,如果它不为空,你传入一个由hasOrganizationId创建的lambda。
由于在Mockito.when中传递的参数和实际的prod代码不相等,Mockito返回默认值Page<AdminUserEntity> adminUserEntitiesByOrganization-对于任何非原始类型都是null。
参见以下测试:

class AdminUserEntity{}

public class LambdaTest {
    public static Specification<AdminUserEntity> hasOrganizationId(String organizationId) {
        return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("organizationId"), organizationId);
    }

    @Test
    void lambdasAreNotEqual() {
        var spec1a = hasOrganizationId("org1");
        var spec1b = hasOrganizationId("org1");

        Assertions.assertThat(spec1a).isNotEqualTo(spec1b);
    }
}

有两种解决方案:

选项1:放松对论证的期望

使用Mockito.any(Specification.class)

Mockito.when(adminUserRepository.findAll(Mockito.any(Specification.class), Mockito.eq(mockAdminUserListRequest.toPageable()))) .thenReturn(mockPageAdminUserEntities);
  • 默认情况下,Mockito使用equals来比较传递给Mockito.when的参数和被测方法中的参数
  • 如果显式指定了一个ArgumentMatcher,则必须显式指定所有ArgumentMatcher(Mockito.eq
    选项2:使用一个实现了equals(和hashCode以确保完整性)的对象

lambda可以修改为Specification的具体子类

public class AdminUserSpecifications {
    public static class AdminUserEntityHasOrganizationIdSpecification implements Specification<AdminUserEntity> {
        private final String organizationId;

        public AdminUserEntityHasOrganizationIdSpecification(String organizationId) {
            this.organizationId = organizationId;
        }

        @Override
        public Predicate toPredicate(Root<AdminUserEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
            return criteriaBuilder.equal( root.get("organizationId"), organizationId);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            AdminUserEntityHasOrganizationIdSpecification that = (AdminUserEntityHasOrganizationIdSpecification) o;
            return Objects.equals(organizationId, that.organizationId);
        }

        @Override
        public int hashCode() {
            return Objects.hash(organizationId);
        }
    } 

    public static Specification<AdminUserEntity> hasOrganizationId(String organizationId) {
        return new AdminUserEntityHasOrganizationIdSpecification(organizationId);
    }
}

在这两个选项中,我假设mockAdminUserListRequest.toPageable()返回的对象正确地实现了equals。如果不是,则添加等于或使用选项1或选项2。

相关问题