SpringBootTestでのテスト

# はじめに
SpringBootで作ったWebアプリにテストコードを実装しました。

# 実行環境
OS：Mac
JUnit：JUnit5.8.2
Java：Amazon Corretto 8
Spring Boot：2.6.1
Gradle：gradle6.8
IDE：intellij idea community edition 2021.1

# テスト環境を構築
## spring-boot-starter-testを使用する
「spring-boot-starter-test」を使用することで、
SpringBoot上のコードをテストするのに必要なライブラリを使用出来ます。

含まれるライブラリは以下の通り

| ライブラリ | 備考 |
|-------|-------|
| JUnit5 | Javaアプリケーションの単体テストのための事実上の標準ライブラリ。|
| Spring Test<br/>Spring Boot Test| Spring Bootアプリケーションのユーティリティ。<br/>統合テストのための機能をサポートしています。 |
| AssertJ　| 高機能なアサーションライブラリ。 |
| Hamcrest　| マッチャーオブジェクトのライブラリ。<br/>オブジェクトの状態をテストするのに利用される。 |
| Mockito　| Javaモックフレームワーク。<br/>テストするクラスが依存しているDI管理下のオブジェクトをモック化したりできる。 |
| JSONassert　| JSON のアサーションライブラリ。 |
| JsonPath　| JSONでもXPATHのように、クエリ形式で要素を検索したりするライブラリ。 |

## build.gradleに依存関係を追加
「spring-boot-starter-test」を依存関係に追加します。

```groovy
dependencies {
    // ・・・
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
	useJUnitPlatform()
}
```

# テストを書く
## テストするWebアプリ
テストするWebアプリは「HelloWorld」を返すものです。
Controllerからリクエストを受け取り、Serviceから「HelloWorld」という文字列を取得してクライアントに返します。

```java
@Controller
public class HelloWorldController {

    private HelloWorldService helloworldService;

    @Autowired
    public HelloWorldController(HelloWorldService helloworldService) {
        this.helloworldService = helloworldService;
    }

    @ResponseBody
    @GetMapping
    public String index() {
        return helloworldService.getString();
    }
}
```

```java
@Service
public class HelloWorldService {

    public String getString() {
        return "HelloWorld";
    }
}
```

## spring-boot-starter-testでのテストの書き方
SpringBootベースのテストをする場合、テストクラスに@&#65520;SpringBootTestを付与します。
@&#65520;SpringBootTestを付与することでJUnitに拡張機能としてSpringExtensionが指定され、
以下の機能を使用できるようになります。

・Spring TestContextフレームワーク
JUnitなどのテストフレームワークにとらわれることなく、テストコードでDI管理やトランザクション機能をサポートします。

・MockMvc（Spring MVC テストフレームワーク）
Spring MVCアプリケーションのテストをサポートします。

## Controllerをテスト
Controllerにリクエストし、「HelloWorld」を取得できることをテストします。
テストクラスには@&#65520;SpringBootTestに加え、@&#65520;AutoConfigureMockMvcも付与します。

@&#65520;AutoConfigureMockMvcを付与するとMockMvcが自動構成されます。
これにより、Controllerや、依存するServiceを通常のサーバー実行時のように扱うことが出来ます。

ControllerへのリクエストはMockMvc、MockMvcRequestBuildersを使用し、
その結果はMockMvcResultMatchersでチェックします。

以下では「http\://localhost/」にリクエストし、
結果、
　httpステータスがOK(200)であること
　コンテンツに「HelloWorld」が返していること
を検証しています。

今回はパラメータなしのリクエストを実行していますが、MockMvcRequestBuildersにURLパラメータを設定することも出来ます。

```java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

@SpringBootTest
@AutoConfigureMockMvc
public class HelloWorldControllerTest {

    @Test
    void HelloWorldが取得されることを確認(@Autowired MockMvc mvc) throws Exception {

        mvc.perform(
                MockMvcRequestBuilders.get("/"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("HelloWorld"));
    }
}
```

## Serviceをテスト
Controllerのテストクラス同様に@&#65520;SpringBootTestを付与します。
こちらではServiceをコンストラクタインジェクションを利用し、DI管理下のインスタンス取得しています。

Serviceのメソッドを通常通り実行し、返却値をアサーションライブラリで検証しています。

```java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
public class HelloWorldServiceTest {

    private final HelloWorldService helloWorldService;

    @Autowired
    public HelloWorldServiceTest(HelloWorldService helloworldService) {
        this.helloWorldService = helloworldService;
    }

    @Test
    void HelloWorldが取得されることを確認() throws Exception {
        String result = helloWorldService.getString();
        assertThat(result).isEqualTo("HelloWorld");
    }
}
```

# 参考文献

https://spring.pleiades.io/spring-boot/docs/current/reference/html/features.html#features.testing

https://spring.pleiades.io/spring-framework/docs/5.3.18/reference/html/testing.html#testing-introduction

https://terasolunaorg.github.io/guideline/5.4.1.RELEASE/ja/UnitTest/ImplementsOfUnitTest/UsageOfLibraryForTest.html