'How I can test Controller with Spring Security
I have next rest controller
@RestController
@AllArgsConstructor
@Slf4j
@RequestMapping("api/v1/user")
public class UserControllerImpl implements UserController {
public final UserService userService;
public final JwtProvider jwtProvider;
public final JwtFilter jwtFilter;
@PostMapping(value = "/registration")
public void registration(@RequestBody UserDto user) {
log.info("Registration user: Got a registration request for user {}", user.getEmail());
userService.save(user);
log.info("Registration user: successful, new user saved in DB");
}
@PreAuthorize("hasAnyRole('ADMIN', 'USER', 'SUPER')")
@PutMapping(value = "/update")
public void update(
ServletRequest servletRequest,
@RequestBody UserDto userDto
) {
log.info("Update user: Got a update request for user {}", userDto);
userService.update(servletRequest, userDto);
log.info("Update user: successful update");
}
}
I use Spring Security with this config:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
public final JwtFilter jwtFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/v1/user/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
And I want to write unit tests for my Controller. I wrote next tests but they don`t work well:
@Import(JwtProvider.class)
@WebMvcTest(value = UserControllerImpl.class)
class UserControllerTest {
@MockBean
private UserServiceImpl userServiceImpl;
@Autowired
private MockMvc mockMvc;
@Test
void registrationRequestExpectOkAndVerifySaveOnce() throws Exception {
doNothing().when(userServiceImpl).save(any());
mockMvc.perform(post("/api/v1/user/registration")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"firstName\": \"mockFirstName\",\"password\": \"mockPassword\"}"))
.andExpect(status().isOk());
verify(userServiceImpl, times(1)).save(any());
}
@Test
void update() throws Exception {
doNothing().when(userServiceImpl).update(any(), any());
mockMvc.perform(put("/api/v1/user/update")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"firstName\": \"mockFirstName\",\"password\": \"mockPassword\"}"))
.andExpect(status().isOk());
verify(userServiceImpl, times(1)).update(any(), any());
}
}
When I started tests, I got status 404. If delete @EnableGlobalMethodSecurity(prePostEnabled = true) in security config, tests are working, but doesn`t work @PreAuthorize. I have some questions:
- why I got 404 if prePostEnabled = true?
- how I can fix this problem?
- if I`m going to write test for method update for user with allowed role and not allowed role, how can I do this?
Solution 1:[1]
You just need to add Spring Security Test dependency as below;
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
</dependency>
And use @WithMockUser annotation to test your controller methods.
@Test
@WithMockUser(roles = {"ADMIN"})
void update() throws Exception {
doNothing().when(userServiceImpl).update(any(), any());
mockMvc.perform(put("/api/v1/user/update")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"firstName\": \"mockFirstName\",\"password\": \"mockPassword\"}"))
.andExpect(status().isOk());
verify(userServiceImpl, times(1)).update(any(), any());
}
But your SecurityConfig has a problem. Having following line disables Spring Security for update and registration urls. You need to correct that first.
antMatchers("/api/v1/user/**").permitAll()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | shazin |
