'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