«1. Введение

Возможность выполнять интеграционные тесты без необходимости в автономной среде интеграции является ценной функцией любого программного стека. Полная интеграция Spring Boot с Spring Security упрощает тестирование компонентов, взаимодействующих с уровнем безопасности.

В этом кратком руководстве мы рассмотрим использование @MockMvcTest и @SpringBootTest для выполнения интеграционных тестов с поддержкой безопасности.

2. Зависимости

Давайте сначала добавим зависимости, которые нам понадобятся для нашего примера:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
</dependency>

spring-boot-starter-web, spring-boot-starter-security и spring-boot Стартеры -starter-test предоставляют нам доступ к Spring MVC, Spring Security и тестовым утилитам Spring Boot.

Кроме того, мы добавим spring-security-test, чтобы получить доступ к аннотации @WithMockUser, которую мы будем использовать.

3. Настройка веб-безопасности

Наша настройка веб-безопасности будет простой. Только аутентифицированные пользователи смогут получить доступ к путям, соответствующим /private/** . Пути, соответствующие /public/**, будут доступны для любого пользователя:

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        auth.inMemoryAuthentication()
         .passwordEncoder(encoder)
         .withUser("spring")
         .password(encoder.encode("secret"))
         .roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
          .antMatchers("/private/**")
          .authenticated()
          .antMatchers("/public/**")
          .permitAll()
          .and()
          .httpBasic();
    }
}

4. Настройка безопасности метода

В дополнение к безопасности на основе URL-пути, которую мы определили в нашем WebSecurityConfigurer, мы можем настроить на основе метода безопасности, предоставив дополнительный файл конфигурации:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfigurer 
  extends GlobalMethodSecurityConfiguration {
}

Эта конфигурация включает поддержку аннотаций до/после Spring Security. Другие атрибуты также доступны, если требуется дополнительная поддержка. Для получения дополнительной информации о безопасности методов Spring ознакомьтесь с нашей статьей на эту тему.

5. Тестирование контроллеров с помощью @WebMvcTest

При использовании подхода аннотации @WebMvcTest с Spring Security MockMvc автоматически настраивается с необходимой цепочкой фильтров, необходимой для тестирования нашей конфигурации безопасности.

Поскольку MockMvc настроен для нас, мы можем использовать @WithMockUser для наших тестов без какой-либо дополнительной настройки: весь контекст. Из-за этого тесты контроллера, использующие @WebMvcTest, будут выполняться быстрее, чем при других подходах.

@RunWith(SpringRunner.class)
@WebMvcTest(SecuredController.class)
public class SecuredControllerWebMvcIntegrationTest {

    @Autowired
    private MockMvc mvc;

    // ... other methods

    @WithMockUser(value = "spring")
    @Test
    public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
        mvc.perform(get("/private/hello").contentType(MediaType.APPLICATION_JSON))
          .andExpect(status().isOk());
    }
}

6. Тестирование контроллеров с помощью @SpringBootTest

При использовании аннотации @SpringBootTest для тестирования контроллеров с Spring Security необходимо явно настроить цепочку фильтров при настройке MockMvc.

Использование статического метода springSecurity, предоставленного SecurityMockMvcConfigurer, является предпочтительным способом сделать это:

7. Тестирование защищенных методов с помощью @SpringBootTest

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SecuredControllerSpringBootIntegrationTest {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mvc;

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

    // ... other methods

    @WithMockUser("spring")
    @Test
    public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
        mvc.perform(get("/private/hello").contentType(MediaType.APPLICATION_JSON))
          .andExpect(status().isOk());
    }
}

@SpringBootTest не требует дополнительной настройки для тестирования защищенных методов. Мы можем просто вызывать методы напрямую и использовать @WithMockUser по мере необходимости:

8. Тестирование с помощью @SpringBootTest и TestRestTemplate

@RunWith(SpringRunner.class)
@SpringBootTest
public class SecuredMethodSpringBootIntegrationTest {

    @Autowired
    private SecuredService service;

    @Test(expected = AuthenticationCredentialsNotFoundException.class)
    public void givenUnauthenticated_whenCallService_thenThrowsException() {
        service.sayHelloSecured();
    }

    @WithMockUser(username="spring")
    @Test
    public void givenAuthenticated_whenCallServiceWithSecured_thenOk() {
        assertThat(service.sayHelloSecured()).isNotBlank();
    }
}

TestRestTemplate — удобный вариант при написании интеграционных тестов для защищенных конечных точек REST.

Мы можем просто автоматически подключить шаблон и установить учетные данные перед запросом защищенных конечных точек:

TestRestTemplate является гибким и предлагает множество полезных параметров, связанных с безопасностью. Для получения более подробной информации о TestRestTemplate ознакомьтесь с нашей статьей на эту тему.

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SecuredControllerRestTemplateIntegrationTest {

    @Autowired
    private TestRestTemplate template;

    // ... other methods

    @Test
    public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
        ResponseEntity<String> result = template.withBasicAuth("spring", "secret")
          .getForEntity("/private/hello", String.class);
        assertEquals(HttpStatus.OK, result.getStatusCode());
    }
}

9. Заключение

В этой статье мы рассмотрели несколько способов выполнения интеграционных тестов с поддержкой безопасности.

Мы рассмотрели, как работать с конечными точками mvccontroller и REST, а также с защищенными методами.

Как обычно, весь исходный код примера можно найти на GitHub.

«