Spring Security и MockMvc - нужно имитировать аутентификацию или принципала

Я использую Spring Security и сталкиваюсь с проблемой написания модульного теста (с использованием MockMvc) для контроллера.

В моем контроллере есть метод, который выглядит примерно так:

@GetMapping
public ResponseEntity<User> getUser(@AuthenticationPrincipal User activeUser){
    String userEmail = activeUser.getEmail();
    return userService.getUser(userEmail);
}

Я получаю ошибку 500.

Другой вариант контроллера, который я пробовал, работает с Postman / Curl:

@GetMapping
public ResponseEntity<User> getUser(OAuth2Authentication authentication){
    String userEmail = (String) authentication.getUserAuthentication().getPrincipal();
    return userService.getUser(userEmail);
}

Моя служба выглядит так:

public ResponseEntity<User> getUser(String email) {
    return userRepository.findByEmail(email)
            .map(record -> ResponseEntity.ok().body(record))
            .orElse(ResponseEntity.notFound().build());
}

В моем модульном тесте для этого метода контроллера у меня есть:

@Test
@WithMockUser(username = "1", password = "pwd", roles = "USER")
public void controller_should_get_user() throws Exception {
    when(userService.getUser("1")).thenReturn(new ResponseEntity(userMock, HttpStatus.OK));
    this.mockMvc.perform(MockMvcRequestBuilders.get("/api/user/")).andExpect(status().isOk());
}

Я получаю следующую ошибку:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
at com.timecloud.user.controller.UserControllerTest.controller_should_get_user(UserControllerTest.java:60)

Как мне передать или издеваться над пользователем с текущей аутентификацией? Спасибо.


person anz    schedule 14.03.2019    source источник


Ответы (2)


NullPointerException идет, потому что ваш тест не может найти ничего для OAuth2Authentication объекта. Есть две вещи, которые вы можете выполнить в своем тестовом примере:

  1. Попробуйте Mocking OAuth2Authentication в каком-нибудь методе setUp.

OR

  1. Если вы используете Spring 4.0+, лучшим решением будет аннотировать метод тестирования с помощью @ WithMockUser

    @Test @WithMockUser(username = "user1", password = "pwd", roles = "USER") public void mytest1() throws Exception { //Your test scenario }

person Yogendra Mishra    schedule 14.03.2019
comment
Обновил мой ответ с использованием @WithMockUser и AuthenticationPrincipal (вместо OAuth2Authentication). Я все еще сталкиваюсь с той же проблемой. - person anz; 14.03.2019
comment
Можете ли вы поделиться кодом для вашего пользовательского сервиса. возможно, вам нужно изменить свое издевательство для пользовательского сервиса на: - when(userService.getUser("1")).thenReturn(userMock); - person Yogendra Mishra; 14.03.2019
comment
обновлено. Но весь смысл насмешек над сервисом состоял в том, чтобы не заботиться о базовой реализации, не так ли? - person anz; 14.03.2019
comment
правильно, но с mockmvc вы проводите интеграционное тестирование. проверьте это: stackoverflow.com / questions / 15203485 / для исх. - person Yogendra Mishra; 14.03.2019

@WithMockUser создает UsernameAuthenticationToken, а не OAuth2Authentication.

Здесь как минимум три решения:

  1. Внедрить OAuth2Authentication mock или экземпляр в контексте безопасности
  2. измените свой метод на public ResponseEntity<User> getUser(Authentication authentication), затем используйте authentication.getName() внутри
  3. используйте существующие инструменты, чтобы применить решение 1. для вас, как в этой библиотеке, которую я написал

Пример использования с раствором 1

@Test
public void test() throws Exception {
    final var storedRequest = mock(OAuth2Request);

    final var principal = mock(Principal.class);
    when(principal.getName()).thenReturn("user");

    final var userAuthentication = mock(Authentication.class);
    when(userAuthentication.getAuthorities()).thenReturn(Set.of(new SimpleGrantedAuthority("ROLE_USER"));
    when(userAuthentication.getPrincipal()).thenReturn(principal);

    final var oauth2Authentication = new OAuth2Authentication(storedRequest, authentication);

    SecurityContextHolder.getContext().setAuthentication(oauth2Authentication);

    // use MockMvc to test a @Controller or unit-test any other secured @Component as usual
}

Пример использования с раствором 3

@Test
@WithMockAuthentication(authType = OAuth2Authentication.class, name = "user", authorities = "ROLE_USER")
public void test() throws Exception {
    // use MockMvc to test a @Controller or unit-test any other secured @Component as usual
}
person ch4mp    schedule 31.03.2020