◆ Spring Boot MockMVCを使った単体テスト

# ★ はじめに

私はクラス名・メソッド名・変数名を考えるのがとても苦手です。
本来はtargetとなるメソッド名_結果や項番となるようにつけるのが望ましいのでしょうが、
仕様書とテストクラスは項番やNo.でマッピングしがちなので、だいたい「test001」のようにつけることが多いです。

今回は前回までに作成したControllerクラスのテストを実施してみます。

# ★ MockMVC
Spring testで提供されている機能。
Controllerのリクエスト～レスポンスまでの一連の流れが簡単に確認できる。

# ★ 実装内容
### build.gradle
テストでMockMVCを使用するため、build.gradleに以下のように記述を追加。

```gradle:build.gradle
dependencies {
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
```

### Controller/Service
ControllerやServiceについては以下記事よりほぼ変更なし。
[◆ Spring Boot+MyBatis+PostgreSQL DB接続①](https://qiita.com/Haru_3/items/eba8fdb296aa809e9546)

### RestApiControllerTest.java（テストクラス）

```java:RestApiControllerTest.java
package com.example.sample.app.controller;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import com.example.sample.app.domain.service.PersonService;
import com.example.sample.domain.entity.Person;
import com.fasterxml.jackson.databind.ObjectMapper;

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

    MockMvc mockMvc;

    @InjectMocks
    RestApiController controller;

    @Test
    public void test001() {
        // パラメータのID
        Integer id = 10;

        // PersonServiceのMockを設定
        Person person = new Person();
        person.setId(id);
        person.setName("ほげみ");
        person.setAge(14);
        person.setHobby("パンケーキ屋さん巡り");

        PersonService mockService = mock(PersonService.class);
        when(mockService.findById(id)).thenReturn(person);

        Person expected = person;

        controller = new RestApiController();
        controller.personService = mockService;

        this.mockMvc = MockMvcBuilders
            .standaloneSetup(controller)
            .build();

        MockHttpServletRequestBuilder getRequest =
                MockMvcRequestBuilders.get("/api/sample/getPerson/" + id);

        try {
            MvcResult result = mockMvc.perform(getRequest)
                    .andDo(print())
                    .andExpect(status().isOk())
                    .andReturn();

            ObjectMapper mapper = new ObjectMapper();
            Person actual = mapper.readValue(result.getResponse().getContentAsString(), Person.class);

            assertEquals(expected.getId(), actual.getId());
            assertEquals(expected.getName(), actual.getName());
            assertEquals(expected.getAge(), actual.getAge());
            assertEquals(expected.getHobby(), actual.getHobby());

        } catch (Exception e) {
            e.printStackTrace();
            fail();
        }
    }
}
```
まずPersonServiceのMockを作成する。今回はMockitoを使用します。
Serviceのレスポンスをそのまま返却するという結構手抜きなAPIなので（処理結果とかエラーコードとかエラー事由とか入れればよかったかも）、イコールassertしたときの予想値となります。

リクエストを作成し、performで投げています。
今回はGETのテストのため、`MockMvcRequestBuilders.get()`と中にURLを設定。POSTの場合は`post()`。

`.andExpect(status().isOk())`でHTTPステータスが成功であることの検証を実施。
`print()`でコンソール上にリクエストやレスポンスなどの情報を出力してくれる。これけっこう感動しました。
ちなみに出力結果はこんな感じ。

```コンソール:
MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /api/sample/getPerson/10
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.example.sample.app.controller.RestApiController
           Method = public com.example.sample.domain.entity.Person com.example.sample.app.controller.RestApiController.getPerson(java.lang.Integer)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

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

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json;charset=UTF-8"]
     Content type = application/json;charset=UTF-8
             Body = {"id":10,"name":"ほげみ","age":14,"hobby":"パンケーキ屋さん巡り"}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
```

実行後はmapperでResponse値を変換してassertで値の検証しています。

# ★ おわりに
ばばばーって書いたテストコードなのであんまりきれいじゃないなって記事を書きながら思いました。
あとせっかくだからPOSTで送った時の確認もしてみたい。

そして前回の続きのDB接続②はたぶん次で実施。