Java 验证使用BCryptPasswordEncoder编码的密码时,断言失败

Java 验证使用BCryptPasswordEncoder编码的密码时,断言失败,java,unit-testing,mockito,Java,Unit Testing,Mockito,我有非常简单的UserCreator服务 @Service public class UserCreator { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; public UserCreator(UserRepository userRepository, PasswordEncoder passwordEncoder) {

我有非常简单的
UserCreator
服务

@Service
public class UserCreator {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    public UserCreator(UserRepository userRepository, PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }

    public CreateUserResult createUser(CreateUserCommand command) {
        validate(command);
        return new CreateUserResult(doCreateUser(command).getId());
    }

    private User doCreateUser(CreateUserCommand command) {
        final var user = User.builder()
                .password(passwordEncoder.encode(command.getPassword()))
                .email(command.getEmail())
                .birthDate(command.getBirthDate())
                .build();
        return userRepository.save(user);
    }
}
我添加了几个单元测试,其中一个测试验证是否创建了用户编码的密码

@ExtendWith(MockitoExtension.class)
public class UserCreatorTest {

    @Mock
    private UserRepository userRepository;
    private UserCreator userCreator;
    private PasswordEncoder passwordEncoder;

    @BeforeEach
    public void setUp() {
        this.passwordEncoder = new BCryptPasswordEncoder();
        this.userCreator = new UserCreator(userRepository, passwordEncoder);
    }

    @Test
    @DisplayName("Created user has encoded password")
    void test2() {
        final var userCaptor = ArgumentCaptor.forClass(User.class);
        when(userRepository.save(any())).thenReturn(User.builder().build());
        final var command = CreateUserCommand.builder()
                .email("test@test.pl")
                .password("test123")
                .birthDate(LocalDate.of(1990, 10, 10))
                .build();
        final var expectedPassword = passwordEncoder.encode(command.getPassword());

        userCreator.createUser(command);

        verify(userRepository, times(1)).save(userCaptor.capture());
        assertTrue(passwordEncoder.matches(expectedPassword, userCaptor.getValue().getPassword()));
    }
}
但是,此测试在
assertTrue()
上失败,这些密码不匹配


为什么会这样?

当我使用
BCryptPasswordEncoder
处理身份验证时,我检查了应用程序的另一部分,它实际上为我解决了这个问题

而不是做

final var expectedPassword = passwordEncoder.encode(command.getPassword());
assertTrue(passwordEncoder.matches(expectedPassword, userCaptor.getValue().getPassword()));
我应该这么做

assertTrue(passwordEncoder.matches(command.getPassword(), userCaptor.getValue().getPassword()));

现在我的测试通过了。

BCryptPasswordEncoder#encode
不是确定性的。它将包含一个随机salt,因此您的
expectedPassword
doCreateUser
中的
passwordEncoder.encode
不一定匹配。使用一个mock
PasswordEncoder
,或者(可能不值得)传入一个mock
SecureRandom
,这样两个调用都匹配。关于调用
新的BCryptPasswordEncoder()
-我确实在调用它,但正如您所看到的,相同的isntance被传递到服务并在单元测试中使用。这里我没有使用两个不同的实例。