Модульное тестирование / вход в Spring MVC с использованием MockMvc

У меня есть очень простое приложение REST, созданное с использованием Spring MVC. (Код доступен на GitHub.) Он имеет простой WebSecurityConfigurer следующим образом:

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .csrf().disable()
            .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .and()
            .authorizeRequests()
                .antMatchers("/user/new").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login").permitAll()
                .successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailureHandler)
                .and()
            .logout()
                .permitAll()
                .logoutSuccessHandler(logoutSuccessHandler);
}

Когда я запускаю приложение, и пользовательские контроллеры, и страницы входа/выхода из системы работают без проблем. Я даже могу модульный тест /user/new через MockMvc. Однако, когда я пытаюсь проверить /login со следующей функцией

@Test
public void testUserLogin() throws Exception {
    RequestBuilder requestBuilder = post("/login")
            .param("username", testUser.getUsername())
            .param("password", testUser.getPassword());
    mockMvc.perform(requestBuilder)
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(cookie().exists("JSESSIONID"));
}

он терпит неудачу следующим образом:

MockHttpServletRequest:
         HTTP Method = POST
         Request URI = /login
          Parameters = {username=[test-user-UserControllerTest], password=[test-user-UserControllerTest-password]}
             Headers = {}

             Handler:
                Type = org.springframework.web.servlet.resource.ResourceHttpRequestHandler

               Async:
   Was async started = false
        Async result = null

  Resolved Exception:
                Type = org.springframework.web.HttpRequestMethodNotSupportedException

        ModelAndView:
           View name = null
                View = null
               Model = null

            FlashMap:

MockHttpServletResponse:
              Status = 405
       Error message = Request method 'POST' not supported
             Headers = {Allow=[HEAD, GET]}
        Content type = null
                Body = 
       Forwarded URL = null
      Redirected URL = null
             Cookies = []

java.lang.AssertionError: Status expected:<200> but was:<405>

Но я довольно пользователь POST до /login работает, когда я запускаю приложение вместо теста. Кроме того, когда я пытаюсь сделать запрос GET или HEAD (как предлагается в строке Headers = {Allow=[HEAD, GET]} логов), на этот раз я получаю 404. Есть идеи о том, что происходит и как я могу это исправить?


person Volkan Yazıcı    schedule 30.01.2015    source источник
comment
Я считаю, что /login URL обрабатывается через фильтр безопасности spring, и я не уверен, что вы настроили его во время настройки теста.   -  person sodik    schedule 30.01.2015


Ответы (4)


Я заметил ссылку на github сейчас. Я считаю, что вам нужно настроить фильтр безопасности и для ваших тестов. Что-то типа

 mockMvc = MockMvcBuilders.webApplicationContextSetup(webApplicationContext)
                .addFilter(springSecurityFilterChain)
                .build();

Могут потребоваться некоторые дополнительные настройки, вы можете проверить http://www.petrikainulainen.net/programming/spring-framework/integration-testing-of-spring-mvc-applications-security/ для вдохновения.

person sodik    schedule 30.01.2015
comment
на этот раз я получаю следующее MockHttpServletResponse с HTTP-кодом 200, но файл cookie отсутствует. Состояние = 200 Сообщение об ошибке = null Заголовки = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[без кеша, без хранения, max-age=0, must-revalidate], Pragma=[без кеша], Expires=[0], X-Frame-Options=[ DENY]} Тип содержимого = null Тело = URL-адрес переадресации = null URL-адрес перенаправления = null Cookies = [] - person Volkan Yazıcı; 30.01.2015

Вы можете использовать весенние классы тестирования безопасности для проверки входа на основе формы. Вам необходимо сделать следующее, чтобы проверить вход на основе формы.

Зависимость от Maven:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <version>4.0.0.M1</version>
    <scope>test</scope>
</dependency>

<repositories>
    <repository>
        <id>spring-snasphot</id>
        <url>https://repo.spring.io/libs-snapshot</url>
    </repository>
</repositories>

Тестовый класс:

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;

RequestBuilder requestBuilder = formLogin().user("username").password("passowrd");
mockMvc.perform(requestBuilder)
    .andDo(print())
    .andExpect(status().isOk())
    .andExpect(cookie().exists("JSESSIONID"));
person Mithun    schedule 30.01.2015
comment
Я все еще получаю 405. Когда я использую .addFilter(springSecurityFilterChain), как предложил @sodik, на этот раз я получаю 200, но файлы cookie отсутствуют. (См. мой комментарий к @sodik.) - person Volkan Yazıcı; 30.01.2015

Следуя моему сообщению здесь: Фильтр безопасности Spring MockMvcBuilders

Мне удалось создать REST API с UsernamePasswordAuthenticationToken для /oauth/token, но мне не удалось создать тест для моего защищенного ресурса (на работающем сервере он работает нормально). Не могли бы вы показать нам свой интерфейс REST?

person Pegazus    schedule 03.02.2015
comment
Весь код доступен на GitHub, см. мой пост. - person Volkan Yazıcı; 03.02.2015

Тестируя SpringSession, я получил файл cookie сеанса и сохранил сеанс во встроенном Redis, выполнив следующие действия:

@Autowired
private SessionRepositoryFilter<?> springSessionRepositoryFilter;
...

@Before
public void setup() {
    mvc = MockMvcBuilders
        .webAppContextSetup(context)
        .addFilters(springSessionRepositoryFilter)
        .apply(springSecurity())
        .build();
}

Надеюсь, поможет.

person jpadilladev    schedule 01.02.2019