Search code examples
javaunit-testingtestingjunitmockito

Is there a way to check if local variable methods have been used with JUnit5 and Mockito?


I've got a service class for the User entity. One of the methods is responsible for changing the user password. And here is the problem. The method looks for a user in the database and stores it in a local variable. Obviously, it's working, but is there a way to write a test to check if the password setter method in the user class was used?

public AuthenticationResponse changeUserPassword(ChangeUserPasswordDto changeUserPasswordDto,
            String jwtToken)
            throws RuntimeException
{
        User user = userRepository.findByEmail(jwtUtil.extractUsername(jwtToken)).get();

        if (!passwordEncoder.matches(changeUserPasswordDto.getPassword(),user.getPassword()))
            throw new InvalidPasswordException("Old password is not matching.");
        if (!changeUserPasswordDto.getNewPassword().equals(changeUserPasswordDto.getNewPasswordConfirmation()))
            throw new NotMatchingPasswordsException("Passwords are not matching.");

        user.setPassword(passwordEncoder.encode(changeUserPasswordDto.getNewPassword()));
        user.setLastCredentialsChangeTime(System.currentTimeMillis());
        return AuthenticationResponse.builder().token(jwtUtil.generateToken(userRepository.save(user))).build();
}

I've been looking for a solution to this problem for half of the day, but I found only one questionable solution using Powermock library. Still, this library hasn't been updated since last year.

There is no possibility of using Mockito.verify() or something because it's not visible outside.

Does it even make sense to write a test for something like this?


Solution

  • Two simplest ways of verifying if the password was changed are in my opinion:

    1. Using a getter of the object returned from the mocked repository. It's the same object instance, so if it's modified inside the tested method, we can see that after the method is called in the test:
    @Test
    void userWithGetter() {
        var email = "[email protected]";
        var password = "admin123";
        var user = new User();
        when(userRepository.findByEmail(email))
                .thenReturn(Optional.of(user));
    
        testedClass.changeUserPassword(email, password);
    
        assertEquals(password, user.getPassword());
    }
    
    1. Similarly, but in case User class does not have a getter for the password, we can spy on the object that we're returning from the mocked repository to verify the setter call:
    @Test
    void userWithoutGetter() {
        var email = "[email protected]";
        var password = "admin123";
        var user = spy(new User());
        when(userRepository.findByEmail(email))
                .thenReturn(Optional.of(user));
    
        testedClass.changeUserPassword(email, password);
    
        verify(user).setPassword(password);
    }
    

    I've prepared a GitHub repository with shortened code reproducing your class under test and included the tests described above - both pass. If any further clarification is needed, I'd be happy to help.