Spring Boot单元测试中BCryptPasswordEncoder返回null问题解决
Spring Boot单元测试中Mockito模拟PasswordEncoder导致密码为null的问题
我在Spring Boot应用里用JUnit和Mockito做单元测试,测试用户注册方法时,创建User对象调用passwordEncoder.encode("password")得到的密码始终是null,导致断言失败。
原始代码
UserServiceImpl.java
@Service public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; public UserServiceImpl(UserRepository userRepository, PasswordEncoder passwordEncoder) { this.userRepository = userRepository; this.passwordEncoder = passwordEncoder; } @Override public User register(User user) { Role role = roleService.findByName(ERole.ROLE_USER); Set<Role> roleSet = new HashSet<>(); user.setFullName(user.getFullName()); user.setEmailAddress(user.getEmailAddress()); user.setPassword(passwordEncoder.encode(user.getPassword())); user.setConfirmPassword(""); user.setEmailVerified(true); roleSet.add(role); user.setRoles(roleSet); if(userRepository.existsByEmailAddress(user.getEmailAddress())) { throw new EmailAlreadyExistsException("Account already exists with this email"); } return userRepository.save(user); } }
UserServiceImplTest.java
@ExtendWith(MockitoExtension.class) class UserServiceImplTest { @InjectMocks private UserServiceImpl userService; @Mock private UserRepository userRepository; @Mock public PasswordEncoder passwordEncoder; @BeforeEach void setUp() { userService = new UserServiceImpl(userRepository, passwordEncoder); } @Test void testIfUserCanRegisterSuccessfully() { User user = new User(1, "admin", "admin@gmail.com", passwordEncoder.encode("password")); when(userRepository.save(any())).thenReturn(user); User theUser = userService.register(user); System.out.println("theUser: " + theUser); assertNotNull(theUser); assertEquals(1, theUser.getId()); assertEquals("admin@gmail.com", theUser.getEmailAddress()); assertTrue(passwordEncoder.matches("password", theUser.getPassword())); } }
SecurityConfig.java
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Value("${spring.security.ant.matchers}") private String[] securityAntMatchers; @Autowired private UserDetailsServiceImpl userDetailsService; @Autowired private JwtAuthenticationEntryPoint authenticationEntryPoint; @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() { return new JwtAuthenticationTokenFilter(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean @Override public AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable().formLogin().disable().httpBasic().disable(); http .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) .and() .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); http .authorizeRequests() .antMatchers(securityAntMatchers) .permitAll() .anyRequest() .authenticated(); } }
测试输出问题
测试时控制台输出的theUser信息如下,不确定怎么在测试中正确处理密码加密:
theUser: User{id=1, fullName='admin', emailAddress='admin@gmail.com', dateJoined=Tue Nov 15 00:06:01 CAT 2022, emailVerified=true, password='null'}
解决方案(修改后代码)
通过在测试中实例化真实的BCryptPasswordEncoder替代Mockito模拟对象,解决密码为null的问题:
修改后的UserServiceImplTest.java关键代码
@BeforeEach void setUp() { // 每次测试前实例化真实的密码编码器 this.passwordEncoder = new BCryptPasswordEncoder(); userService = new UserServiceImpl(userRepository, passwordEncoder); } @Test void testIfUserCanRegisterSuccessfully() { User user = new User(1, "admin", "admin@gmail.com","password"); when(userRepository.save(any())).thenReturn(user); User created = userService.register(user); assertEquals(created.getPassword(), user.getPassword()); assertEquals(created.getEmailAddress(), user.getEmailAddress()); verify(userRepository).save(any()); }
问题说明
原来的测试中用@Mock模拟了PasswordEncoder,但没有为encode方法设置返回值,所以调用passwordEncoder.encode("password")时默认返回null。除了使用真实编码器,也可以给模拟对象添加返回值设置:when(passwordEncoder.encode(anyString())).thenReturn("加密后的测试密码"),两种方式都能解决null问题。
内容的提问来源于stack exchange,提问作者ServletException
相关产品推荐
相关产品推荐

