Micronaut入門2〜ユニットテスト〜


# 概要
[Micronaut入門1〜導入〜](https://qiita.com/tMatSuZ/items/98632b00a5428d85a4a0)のつづき
前回作成したHelloControllerを少し拡張してユニットテストを行う。
Micronaut拡張モジュールのMicronaut Testを使用する。

# Micronaut Testとは
Micronaut用のテストフレームワーク

- JUnit 5
- Spock

どちらかでテストを行うことができる。
今回はJUnit 5を使用してテストする。

# 実装クラスの修正・追加
###### コントローラーの修正
HelloControllerにテスト用のメソッドを追加する。

```java:HelloController.java
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import my.app.service.HelloService;

@Controller("/hello")
public class HelloController {
    HelloService helloService;

    HelloController(HelloService helloService) {
        this.helloService = helloService;
    }

    @Get(produces = MediaType.TEXT_PLAIN)
    public String index() {
        return "hello world!!";
    }

    // 複数パラメータでのテスト用に追加
    @Get(uri = "/{path}",processes = MediaType.TEXT_PLAIN)
    public String index(String path) {
        return path;
    }

    // サービスクラスの処理Mock確認のために追加
    @Get(uri = "/compute/{number}",processes = MediaType.TEXT_PLAIN)
    public String compute(Integer number) {
        // テスト時にcomputeメソッドをMockにする。
        return String.valueOf(helloService.compute(number));
    }
}
```

###### サービスクラスの追加
```java:HelloService
public interface HelloService {
    public Integer compute(Integer num);
}
```
```java:HelloServiceImpl
import javax.inject.Singleton;

@Singleton
public class HelloServiceImpl implements HelloService {
    @Override
    public Integer compute(Integer num) {
        return num * 4;
    }
}
```

# ユニットテスト
###### 依存ライブラリの追加
```gradle:build.gradle
dependencies {
    ・・・
    testAnnotationProcessor "io.micronaut:micronaut-inject-java"
    testCompile "io.micronaut.test:micronaut-test-junit5"
    testCompile "org.junit.jupiter:junit-jupiter-params"
    testCompile "org.mockito:mockito-core:2.24.5"
    testRuntime "org.junit.jupiter:junit-jupiter-engine"
}
```

###### ユニットテストの追加
```java:HelloControllerTest.java
import io.micronaut.http.HttpRequest;
import io.micronaut.http.client.RxHttpClient;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.test.annotation.MicronautTest;
import io.micronaut.test.annotation.MockBean;
import my.app.service.HelloService;
import my.app.service.HelloServiceImpl;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

import javax.inject.Inject;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

@MicronautTest
public class HelloControllerTest {
    // RxHttpClientでHelloControllerをテストする
    @Inject
    @Client("/")
    RxHttpClient client;

    @Inject
    HelloService helloService;

    @Test
    void testHelloIndex() {
        final String result = client.toBlocking().retrieve(HttpRequest.GET("/hello"), String.class);
        assertEquals(
                "hello world!!",
                result
        );
    }

    @ParameterizedTest
    // パラメータを複数渡して同一ソースで複数回テストすることができる
    @ValueSource(strings = { "racecar", "radar" })
    void testHelloIndexPath(String path) {
        final String result = client.toBlocking().retrieve(HttpRequest.GET("/hello/" + path), String.class);
        assertEquals(
                path,
                result
        );
    }

    @ParameterizedTest
    @CsvSource({"2,4", "3,9"})
    void testComputeNumToSquare(Integer num, Integer square) {

        // Mockの振る舞いを設定する
        when(helloService.compute(num))
                .then(invocation -> Long.valueOf(Math.round(Math.pow(num, 2))).intValue());

        // コントローラーを呼び出して結果を取得する
        final Integer result = client.toBlocking().retrieve(HttpRequest.GET("/hello/compute/" + num), Integer.class);

        assertEquals(
                square,
                result
        );
        verify(helloService).compute(num);
    }

    // MockBeanを使用してMock定義
    @MockBean(HelloServiceImpl.class)
    HelloService mathService() {
        return mock(HelloService.class);
    }
}
```

# 所感
JUnitをしようしてのユニットテストなので導入障壁は低そう。
Micronautの起動が早いので、テストも軽快でよかった。
# 参考
https://micronaut-projects.github.io/micronaut-test/latest/guide/index.html