Search code examples
javaunit-testingspring-securitymockito

How to mock password encoder using Mockito


I'm using Spring security and would like to unit test the password returned by my service. However, as this is encrypted, I'm trying to mock the method that does this encription which is inside the class which extends from WebSecurityConfigurerAdapter.

@Override
public void configure(AuthenticationManagerBuilder auth){
     auth.userDetailsService(userDetailsService).passwordEncoder(NoOpPasswordEncoder.getInstance());        
}

So I'm trying something like this

@Autowired
AuthenticationManagerBuilder auth;


static class PasswordEncoderTest implements PasswordEncoder {
    @Override
    public String encode(CharSequence charSequence) {
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return charSequence.toString().equals(s);
    }
}

@Test
void testCreateUser() throws Exception {

    UserCreateDto userCreateDto = new UserCreateDto("user", "test", "[email protected]", "[email protected]", "123456", "basic");
    User userMocked = new User(userId, "user", "test", "[email protected]", "[email protected]", "123456", "basic");

    PasswordEncoderTest passwordEncoderTest = new PasswordEncoderTest();
    passwordEncoderTest.encode("123456");
    when(auth.userDetailsService(myUserDetailsService).passwordEncoder(NoOpPasswordEncoder.getInstance())).thenReturn(auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoderTest));

    userCreateDto = userCommandService.createUser(userCreateDto);

    assertEquals(userMocked.getPassword(), userCreateDto.getPassword());
}
  

But it fails with DaoAuthenticationConfigurer cannot be returned by generateToken() generateToken() should return String

Not sure if the approach is correct and if it is what I may be doing wrong.

Thank you.

UPDATE

https://github.com/francislainy/adverts-backend/tree/dev_jwt

Based on my conversation with Plalx, It seems I should be injecting password encoder rather than bycript and have different configurations between the main and test classes. Will try that and get back here with further updates.


Solution

  • It's working now. Rather than mocking the hashing, having two different beans, one under Configuration and the other under TestConfiguration did the trick. The bean for the tests doesn't not have hashing.

    Config for the main application:

    @Configuration
    public class WebConfig {
    
        @Bean public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }
    

    Config for the tests:

    @TestConfiguration
    public class TestConfig {
    
        @Bean public PasswordEncoder passwordEncoder() {
           return NoOpPasswordEncoder.getInstance() ;
        }
    }
    

    Test:

    @Import({TestConfig.class})
    @WebMvcTest(UserCommandService.class)
    class UserCommandServiceTest {
    
    @MockBean
    UserRepository userRepository;
    
    @Autowired
    private UserCommandService userCommandService;
    
    @MockBean
    private MyUserDetailsService myUserDetailsService;
    
    @MockBean
    private JwtUtil jwtUtil;
    
    @Test
    void testCreateUser() {
    
        User userMocked = new User(userId, "user", "test", "[email protected]", "123456", "[email protected]", "basic");
        UserCreateDto userCreateDto = new UserCreateDto("user", "test", "[email protected]", "123456", [email protected]", "basic");
    
        when(userRepository.save(any(User.class))).thenReturn(userMocked);
    
        userCreateDto = userCommandService.createUser(userCreateDto);
        assertEquals(userMocked.getPassword(), userCreateDto.getPassword());
    }
    
    }
    

    PS: Don't forget to @Import the TestConfig class above the class name.

    And finally the Service class

    @Service
    public class UserCommandServiceImpl implements UserCommandService {
    
    private final UserRepository userRepository;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    public UserCommandServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    @Override
    public UserCreateDto createUser(UserCreateDto userCreateDto) {
        User user = new User();
        user.setFirstname(userCreateDto.getFirstname());
        user.setLastname(userCreateDto.getLastname());
        user.setEmail(userCreateDto.getEmail());
        user.setUsername(userCreateDto.getUsername());
        user.setPassword(userCreateDto.getPassword());
    
        user = userRepository.save(user);
    
        return new UserCreateDto(user.getId(), user.getFirstname(), user.getLastname(), user.getUsername(), passwordEncoder.encode(user.getPassword()), user.getEmail(), user.getRole());
    }
    }