【问题标题】:How to mock password encoder using Mockito如何使用 Mockito 模拟密码编码器
【发布时间】:2022-01-13 23:27:29
【问题描述】:

我正在使用 Spring 安全性并希望对我的服务返回的密码进行单元测试。但是,由于这是加密的,我试图模拟执行此加密的方法,该方法位于从 WebSecurityConfigurerAdapter 扩展的类中。

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

所以我正在尝试这样的事情

@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", "user@gmail.com", "user@gmail.com", "123456", "basic");
    User userMocked = new User(userId, "user", "test", "user@gmail.com", "user@gmail.com", "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());
}
  

但它失败了DaoAuthenticationConfigurer cannot be returned by generateToken() generateToken() should return String

不确定该方法是否正确以及我可能做错了什么。

谢谢。

更新

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

根据我与 Palx 的对话,我似乎应该注入密码编码器而不是 bycript,并且在主类和测试类之间有不同的配置。将尝试并返回此处进行进一步更新。

【问题讨论】:

  • 没有看到createUser 的实现是不可能的,但是你的when(...) 看起来好像不应该工作。

标签: java unit-testing spring-security mockito


【解决方案1】:

如果您的意思是集成测试...我真的无法尝试,但我认为您可以将PasswordEncoder 配置为@Bean,然后覆盖bean 配置以使用非编码PasswordEncoder .

例如

@Configuration
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired private PasswordEncoder passwordEncoder;

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

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

然后在您的测试中,您将有一个 bean 定义,例如:

@Bean public PasswordEncoder passwordEncoder() {
    return new PasswordEncoderTest();
}

可能会有一些调整,但就是这样。


请注意,如果使用configure 的唯一原因是覆盖协作者,那么您可以删除configure 块,而是定义PasswordEncoderUserDetailsService bean。

【讨论】:

  • 您好,谢谢您的回答。不过,这条线上会发生什么变化? when(auth.userDetailsService(myUserDetailsService).passwordEncoder(NoOpPasswordEncoder.getInstance())).thenReturn(auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoderTest));
  • 当我将 bean 添加到测试类时,我得到了这个错误java.lang.IllegalStateException: Test classes cannot include @Bean methods
  • 您可能必须在另一个类上定义 bean 并使用 @SpringBootTest(classes = { TestConfig.class })。我认为可以完全删除模拟行(调用时),因为您将使用标准UserDetailsService,该标准将配置有假TestPasswordEncoder
  • 谢谢。我已经为 bean 创建了类并删除了 when 行,但我现在得到了Invalid bean definition with name 'passwordEncoder'
  • 酷,非常感谢。这说得通。我现在需要离开,但我会在白天回到它,希望我能够实现它。我需要检查为什么没有调用测试配置,然后更改它和主配置之间的实现。
【解决方案2】:

它现在正在工作。而不是模拟散列,有两个不同的bean,一个在Configuration 下,另一个在TestConfiguration 下就可以了。 用于测试的 bean 没有散列

主应用配置:

@Configuration
public class WebConfig {

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

测试配置:

@TestConfiguration
public class TestConfig {

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

测试:

@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", "user@gmail.com", "123456", "user@gmail.com", "basic");
    UserCreateDto userCreateDto = new UserCreateDto("user", "test", "user@gmail.com", "123456", user@gmail.com", "basic");

    when(userRepository.save(any(User.class))).thenReturn(userMocked);

    userCreateDto = userCommandService.createUser(userCreateDto);
    assertEquals(userMocked.getPassword(), userCreateDto.getPassword());
}

}

PS:别忘了在类名上方@Import TestConfig 类。

最后是服务类

@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());
}
}

【讨论】:

    猜你喜欢
    • 2016-06-09
    • 1970-01-01
    • 1970-01-01
    • 2013-04-23
    • 2011-02-24
    • 2016-05-08
    • 2012-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多