1. 序章

スタンドアロンの統合環境を必要とせずに統合テストを実行する機能は、あらゆるソフトウェアスタックにとって貴重な機能です。 SpringBootとSpringSecurityのシームレスな統合により、セキュリティレイヤーと相互作用するコンポーネントのテストが簡単になります。

このクイックチュートリアルでは、@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 starterは、Springへのアクセスを提供しますMVC、Spring Security、およびSpringBootテストユーティリティ。

さらに、使用する @WithMockUser アノテーションにアクセスするために、 spring-security-testを導入します。

3. Webセキュリティ構成

Webセキュリティの構成は簡単です。 / 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. メソッドのセキュリティ構成

WebSecurityConfigurer で定義したURLパスベースのセキュリティに加えて、追加の構成ファイルを提供することで、メソッドベースのセキュリティを構成できます。

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

この構成により、SpringSecurityの事前/事後注釈のサポートが有効になります。 追加のサポートが必要な場合は、他の属性も使用できます。 Spring Method Securityの詳細については、トピックに関するの記事を参照してください。

5. @WebMvcTestを使用したコントローラーのテスト

SpringSecurityで@WebMvcTestアノテーションアプローチを使用する場合、 MockMvcは、セキュリティ構成をテストするために必要なフィルターチェーンで自動的に構成されます。

MockMvc が構成されているため、追加の構成なしでテストに@WithMockUserを使用できます。

@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());
    }
}

@WebMvcTest を使用すると、コンテキスト全体ではなくWebレイヤーのみをインスタンス化するようにSpringBootに指示されることに注意してください。 このため、@ WebMvcTestを使用するコントローラーテストは、他のアプローチよりも高速に実行されます。

6. @SpringBootTestを使用したコントローラーのテスト

@ Spring BootTest アノテーションを使用してSpringセキュリティでコントローラーをテストする場合、MockMvcを設定するときにフィルターチェーンを明示的に構成する必要があります。

これを行うには、SecurityMockMvcConfigurerによって提供される静的なspringSecurityメソッドを使用することをお勧めします。

@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());
    }
}

7. @SpringBootTestを使用したセキュリティで保護されたメソッドのテスト

@SpringBootTest は、セキュリティで保護されたメソッドをテストするために追加の構成を必要としません。 メソッドを直接呼び出し、必要に応じて@WithMockUserを使用できます:

@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();
    }
}

8. @SpringBootTestおよびTestRestTemplateを使用したテスト

TestRestTemplate は、セキュリティで保護されたRESTエンドポイントの統合テストを作成するときに便利なオプションです。

保護されたエンドポイントを要求する前に、テンプレートを自動配線し、資格情報を設定するだけです。

@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());
    }
}

TestRestTemplate は柔軟性があり、多くの便利なセキュリティ関連のオプションを提供します。 TestRestTemplate の詳細については、トピックに関するの記事をご覧ください。

9. 結論

この記事では、セキュリティ対応の統合テストを実行するいくつかの方法について説明しました。

mvccontrollerとRESTエンドポイント、およびセキュリティで保護されたメソッドを操作する方法を確認しました。

いつものように、ここでの例のすべてのソースコードは、GitHub見つけることができます。