繁体   English   中英

Mockito 模拟从存储库返回 Null 值

[英]Mockito mock returning Null value from repository

我正在尝试使用JUNIT5测试我的服务实现。 我不太明白出了什么问题,但我的UserRepository似乎没有返回值。

我已经测试过 UserRepository 是否被用于: verify(repository, times(1)); 响应是“需要但未调用......实际上,与此模拟的交互为零”

这是测试 Class:

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

    @Mock
    UserRepository repository;
    @InjectMocks
    UserServiceImpl userServiceImpl;

    UserModel inputUserModel;

    @BeforeEach
    public void setUp() throws Exception {
        inputUserModel = new UserModel();
        inputUserModel.setEmail("testemail@gmail.com");
        inputUserModel.setFirstName("john");
        inputUserModel.setLastName("doe");
        inputUserModel.setPassword("test");
        inputUserModel.setMatchPassword("test");

        User inputUser = User.builder()
                .email(inputUserModel.getEmail())
                .firstName(inputUserModel.getFirstName())
                .lastName(inputUserModel.getLastName())
                .password(inputUserModel.getPassword())
                .timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).userID(1)
                .build();

        User outputUser = User.builder().
                email(inputUserModel.getFirstName()).
                password(inputUserModel.getLastName()).
                lastName(inputUserModel.getFirstName())
                .firstName(inputUserModel.getFirstName())

                .timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).userID(1).build();

        Mockito.when(repository.save(inputUser)).thenReturn(outputUser);

    }

    @Test
    public void whenSaveUser_ThenUserHasID(){

        Assertions.assertEquals(1, userServiceImpl.saveUser(inputUserModel).getUserID());
    }
}

我得到的错误:

org.mockito.exceptions.misusing.PotentialStubbingProblem: 
Strict stubbing argument mismatch. Please check:
 - this invocation of 'save' method:
    repository.save(
    User(userID=0, email=testemail@gmail.com, timeCreated=2022-12-14 03:18:24.0435578, password=test, firstName=john, lastName=doe)
);
    -> at com.jschwery.securitydemo.service.Implementations.UserServiceImpl.saveUser(UserServiceImpl.java:37)
 - has following stubbing(s) with different arguments:
    1. repository.save(
    User(userID=1, email=testemail@gmail.com, timeCreated=2022-12-14 03:18:24.0235563, password=test, firstName=john, lastName=doe)
);

我正在为其创建测试的服务 Class:

public class UserServiceImpl implements UserService {

    UserRepository  userRepository;

    @Autowired
    public UserServiceImpl(UserRepository repository){
        this.userRepository = repository;
    }

    @Override
    public User saveUser(UserModel userModel) {
        if(!Objects.equals(userModel.getPassword(), userModel.getMatchPassword())){
            throw new UserException("Passwords do not match");
        }
        User user = User.builder().
                email(userModel.getEmail()).
                firstName(userModel.getFirstName()).
                lastName(userModel.getLastName()).
                password(userModel.getPassword()).
                timeCreated(Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault()))).build();
        User returnedUser = userRepository.save(user);
        System.out.println(returnedUser.getEmail());
        System.out.println("userID" + returnedUser.getUserID());
        return returnedUser;
    }
}

谢谢阅读: :)

我猜您的User class 在userID字段或所有字段上定义了一个equals / hashCode 当您在setUp方法中模拟您的存储库时,您定义了当使用您指定的确切object 调用该方法时应该执行的操作,将给定的 object 与通过调用equals / hashCode方法在模拟中指定的 object 进行比较。

您在setUp方法中将User userID定义为具有用户 ID 1 ,但在您的UserServiceImpl中, userID从未设置过(因为它是由持久层生成的)。 因此,永远不会调用equals / hashCode检查以确定 Mockito 是否应该执行存根逻辑,因为UserServiceImpl传递的 object 永远不会等于您在模拟中定义的那个。

有几种方法可以解决这个问题。

1)集成测试

我的建议是将您的 Unit-Test 转换为@SpringBootTest 测试模拟行为没有任何价值。 如果此测试是集成测试,您将测试存储库是否将User插入数据库并使用您的断言生成 ID。

2)单元测试

如果你想模拟存储库,你应该为输入参数使用any()匹配器,然后做存储库会做的事情,即设置 id:

  when(repository.save(any(User.class))).thenAnswer(invocation -> {
            final User entity = invocation.getArgument(0);
            ReflectionTestUtils.setField(entity, "userID", RandomUtils.nextLong());
            return entity;
        });

然后我会断言, userID不是 null,因为userID将像在生产中一样随机生成:

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

    @Mock
    private UserRepository repository;

    @InjectMocks
    private UserServiceImpl userServiceImpl;

    @Test
    void saveUser_userHasID(){
        // Arrange
        final UserModel inputUserModel = new UserModel();
        inputUserModel.setEmail("testemail@gmail.com");
        inputUserModel.setFirstName("john");
        inputUserModel.setLastName("doe");
        inputUserModel.setPassword("test");
        inputUserModel.setMatchPassword("test");

        when(repository.save(any(User.class))).thenAnswer(invocation -> {
            final User entity = invocation.getArgument(0);
            ReflectionTestUtils.setField(entity, "userID", RandomUtils.nextLong());
            return entity;
        });

        // Act
        final User user = userServiceImpl.saveUser(inputUserModel);

        // Assert
        assertThat(user.getUserID()).isNotNull();
    }
}

我强烈建议您使用 AssertJ 作为您的断言,因为它提供了流畅的断言和比 JUnit 更多的断言方法。

tl;博士

Spring 数据无法通过带有模拟的简单单元测试生成用户 ID,您将需要在集成测试中运行 spring 上下文。

细节

有两个字段导致您遇到错误: UserIDtimeCreated

  • 对于timeCreated字段:在您的测试中,您创建了一个用于模拟的inputUser object。 问题是这个 object inputUser不是你的方法在测试saveUser(UserModel userModel)下使用的那个你可以清楚地看到时间戳在两个对象之间的错误中不同。
  • 对于UserID字段:您再次期望模拟使用inputUser object。此 object 的 userID = 1。但在实际方法saveUser(UserModel userModel)中,此字段未明确设置,因此默认值将设置为0您也可以在错误中看到这一点。

现在,由于您的测试用例whenSaveUser_ThenUserHasID旨在验证保存的记录是否具有 ID,因此您无法使用模拟来完成。 该 ID 由您正在使用的 Spring 数据生成,并且让它运行,您应该使用和集成测试在您的测试中运行您的 spring 上下文,以便您的 spring 数据能够为您的用户生成一个 ID, 这里是之前的一个问题将对您有所帮助。 有了这个,您既不需要测试中的inputUser也不需要模拟。 (您可以使用来自@DataJpaTest 5 的 @DataJpaTest 或@SpringBootTest ...)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM