SpringBootテストの頻出アノテーション

SpringBootのテストを行う上で自分が使ってきたものを備忘録的な感じで記録する

### 単体テスト
#### @RunWith
いろんなRunnerクラスを指定できる
Runnerクラスによってどのようなテストを行うか制御する。

###### @RunWith(SpringRunnner.class)
DIを利用してテストするときに必要

~~~java
@RunWith(SpringRunnner.class)
public class SampleClassTest {

  @Autowired
  private HogeService hogeService; //DIされているサービス

}
~~~

- Autowiredするクラスをモックしたいときには@Autowiredは使わず@MockBeanに書き換える

###### @RunWith(MockitoJUnitRunner.class)
テスト対象のクラスの中でモックしたいクラスがある場合に必要
※staticメソッドのモック化はできないので注意

~~~java
@RunWith(MockitoJUnitRunner.class)
public class SampleClassTest {

  @Mock
  private HogeService hogeService; //動きそうにないクラス

  @Test
  public void 正常系() {

    Fuga fugaMock = mock(Fuga.class);
    when(hogeService.get()).thenReturn(fugaMock);
    ///HogeServiceのget()の戻り値はFugaクラスってことにする

    ...

  }
}
~~~

- SpringBoot特有の処理はなし。
- @Mockとmock()メソッドは共にモック化する処理。

###### @RunWith(PowerMockRunner.class)
staticメソッドをモックしなければいけないときに必要

~~~java
@RunWith(PowerMockRunner.class)
@PrepareForTest({HogeService.class}) //staticメソッドを含むクラス
public class SampleClassTest {

  @Before
  public void setUp() {
    PowerMockito.mockStatic(HogeService.class);
  }

  @Test
  public void 正常系() {

    HogeService hogeMock = mock(HogeService.class);
    Fuga fugaMock = mock(Fuga.class);
    when(hogeMock.get()).thenReturn(fugaMock);
    ///HogeServiceのget()の戻り値はFugaクラスってことにする

    ...

  }
}
~~~

#### @DataJpaTest
エンティティとレポジトリ間だけでのテストをサポートしてくれる。
@Entityと@RepositoryのついたクラスApplicationContextへロードする。

#### @AutoConfigureTestDatabase
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)とすることでアプリケーションで設定されているDBを使うことができる。

#### @TestExecutionListeners
テストを監視し、様々な機能を提供するlistenerを設定できる。

~~~java
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestExecutionListeners({
    TransactionalTestExecutionListener.class,
    DbUnitTestExecutionListener.class})
@DbUnitConfiguration(
    dataSetLoader = ReplacementCsvDataSetLoader.class
)
public class RepositoryTest {

  @DatabaseSetup("/sample/")
  @Test
  public void 正常系() {

    ...

  }
}
~~~

```table-ordering.txt
sample_table
```
```sample_table.csv
id,name,age
1,Tanaka,20
2,Suzuki,24
```

- TransactionalTestExecutionListenerはトランザクション管理機能を提供する。
- DbUnitTestExecutionListenerは@DatabaseSetupなどDB関連の機能を提供する。
- @DatabaseSetupはresource下のディレクトリを指定する。
　指定されたディレクトリのtable-ordering.txtが読みこまれる。
　今回はsample_tableと記述しているの、同ディレクトリのsample_table.csvが読み込まれ、テスト用データが用意される
- @DbUnitConfiguration(dataSetLoader = ReplacementCsvDataSetLoader.class)はDBをCSV形式で扱うようにする。
  @DatabaseSetupをCSVで行うなら必須

> その他指定されそうなlistener
>
> ServletTestExecutionListener：
> WebApplicationContextのテストをサポートするモックサーブレットAPIを設定する機能を提供している。
>
> DirtiesContextBeforeModesTestExecutionListener：
> テストで使用するDIコンテナのライフサイクル管理機能を提供している。 テストクラスまたはテストメソッドの実行前に呼び出される。
>
> DependencyInjectionTestExecutionLiLstener：
> テストで使用するインスタンスへのDI機能を提供している。
>
> SqlScriptsTestExecutionListener：
> @Sqlアノテーションで指定されているSQLを実行する機能を提供している。

### 結合テスト

#### @SpringBootTest
SpringBootの機能を提供してくれる(application.propertiesとかymlとか読んでくれる)
デフォルトではアプリを起動しないが、設定すれば起動するようになる。
>  @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

~~~java
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestExecutionListeners(
        listeners = {
                TransactionalTestExecutionListener.class,
                DbUnitTestExecutionListener.class},
        mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
@DbUnitConfiguration(
        dataSetLoader = ReplacementCsvDataSetLoader.class
)
public class FunctionalTest {

  @DatabaseSetup("/sample/")
  @Test
  public void 正常系() {

    Resource sampleJson = resourceLoader.getResource("/response/正常系.json");
    String jsonText = FileUtils.readFileToString(responseResource.getFile(),UTF_8);

    ResponseEntity actual = testRestTemplate.exchange(
        "/top",
        HttpMethod.GET,
        new HttpEntity<>(null,null),String.class
    );
    String actualText = actual.getBody().toString();
    ...//assertやVerify

  }
}
~~~
- RestAPIのテストのサンプル
- resouece下にjsonを配置し期待値を取得
- testRestTemplateで実行し、実行値を取得

#### @AutoConfigureMockMvc
SpringMVCの動作を再現する。
Controller↔︎View間での通信をテストできるようになる。

~~~java
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestExecutionListeners(
        listeners = {
                TransactionalTestExecutionListener.class,
                DbUnitTestExecutionListener.class},
        mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
)
@DbUnitConfiguration(
        dataSetLoader = ReplacementCsvDataSetLoader.class
)
@AutoConfigureMockMvc
public class FunctionalTest {

  @DatabaseSetup("/sample/")
  @Test
  public void 正常系() {

      MvcResult result = mockMvc.perform(get("/top")) //URL指定
          .andExpect(status().isOk())  //HTTPステータスの確認
          .andExpect(view().name("top")) //HTMLの確認
          .andReturn();

    ...

  }
}
~~~

- /topを叩いてステータス200が返ってきてtop.htmlが見れてたらOKって感じのもの