Quarkus 2.0で導入された継続的テストを試してみる

# はじめに
2021年6月に[Quarkus 2.0がリリース](https://ja.quarkus.io/quarkus2/)されました。Quarkusは、Kubernetesネイティブのマイクロサービス向けJavaフレームワークで、Red Hat社がサポートし、オープンソースとして提供されています。

Quarkus 2.0では新たに「継続的テスト」という機能が盛り込まれました。Quarkusには元々JUnitやREST APIのテストライブラリであるREST Assuredが組み込まれており、加えて、``mvn test``でのテスト開始前に自動的にアプリケーションを起動してREST APIのテストを可能にするといった機能も備えているなど、テストのサポートが充実していました。

Quarkus 2.0で導入された「継続的テスト」は、コードが変更された(保存された)ことをトリガーとして、自動的にJUnitのテストケースを実行する機能になります。変更されたコードとJUnitテストケースの関連が検出され、関連するテストケースのみが実行されるようになっています。また、簡単な操作でテストを再実行したり、継続的テストを一時的に止めたりといったことも可能になっています。

当記事ではQuarkus 2.0の継続的テストをQuarkusのガイドに沿って実際に試し、どのような結果が得られるのかを見てみます。


# 参考

- [QUARKUS - 初めてのアプリケーションの作成](https://ja.quarkus.io/guides/getting-started)
- [QUARKUS - 継続的テスト](https://ja.quarkus.io/guides/continuous-testing)


# 環境

- Quarkus 2.0.2
- JDK 11

```
❯ java -version
openjdk version "11.0.10" 2021-01-19
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.10+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.10+9, mixed mode)
```

# 実施手順・結果
まず、Quarkus 2.0のサンプルアプリケーションを作成します。

サンプルアプリケーションの作成については、[QUARKUS - 初めてのアプリケーションの作成](https://ja.quarkus.io/guides/getting-started)に沿って、「[8. テスト](https://ja.quarkus.io/guides/getting-started#%E3%83%86%E3%82%B9%E3%83%88)」まで進めてください。

サンプルアプリケーションが作成できたら、サンプルアプリケーションのルートディレクトリで以下のコマンドを実行します。

```
❯ mvn quarkus:dev

(中略)
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2021-07-28 11:05:57,284 INFO  [io.quarkus] (Quarkus Main Thread) quarkus2-getting-started 1.0.0-SNAPSHOT on JVM (powered by Quarkus 2.0.2.Final) started in 1.285s. Listening on: http://localhost:8080
2021-07-28 11:05:57,298 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2021-07-28 11:05:57,298 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, resteasy, smallrye-context-propagation]

--
Tests paused, press [r] to resume, [w] to open the browser, [h] for more options>
```

Quarkusアプリケーションが起動し、``Tests paused`` と表示されている通り、テスト実行が待機(ポーズ)の状態となります。``r``キーを押すと継続的テストモードが再開(開始)されます。押した結果が以下になります。

```
--
All 2 tests are passing (0 skipped), 2 tests were run in 1802ms. Tests completed at 11:10:12.
Press [r] to re-run, [v] to view full results, [p] to pause, [h] for more options>
```

2件のテストが実行され、全てがパスした(正常動作した)という結果が表示されました。

また、この状態ではファイルを変更して保存すると関連するテストが自動的に実行されるようになっています。GreetingResourceクラスを編集・保存し、わざとテストを失敗させ、どのような結果になるかを確認します。修正対象はデフォルトでは``src/main/java/org/getting/started/GreetingResource.java``というファイルになります。hello()メソッド(/helloエンドポイント)は「hello」という文字列を返すようになっていますが、これを別の文字列に書き換えることでテストを失敗させます。書き換えた後、ファイルを保存します。

```java
package org.acme.getting.started;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.jboss.resteasy.annotations.jaxrs.PathParam;

@Path("/hello")
public class GreetingResource {

    @Inject
    GreetingService service;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/greeting/{name}")
    public String greeting(@PathParam String name) {
        return service.greeting(name);
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";  // returnする文字列を適当に書き換える
    }
}
```

ファイルを保存すると、テストが自動的に実行され、以下のような結果が得られます。2件のテストが実行され、1件がfailedになっています。また、どういった理由でテストがfailしたかが表示されています。

```
2021-07-28 12:39:29,192 ERROR [io.qua.test] (Test runner thread) ==================== TEST REPORT #6 ====================
2021-07-28 12:39:29,192 ERROR [io.qua.test] (Test runner thread) Test GreetingResourceTest#testHelloEndpoint() failed 
: java.lang.AssertionError: 1 expectation failed.
Response body doesn't match expectation.
Expected: is "hello"
  Actual: hello world

        at io.restassured.internal.ValidatableResponseImpl.body(ValidatableResponseImpl.groovy)
        at org.acme.getting.started.GreetingResourceTest.testHelloEndpoint(GreetingResourceTest.java:20)

2021-07-28 12:39:29,192 ERROR [io.qua.test] (Test runner thread) >>>>>>>>>>>>>>>>>>>> 1 TEST FAILED <<<<<<<<<<<<<<<<<<<<


--
1 test failed (1 passing, 0 skipped), 2 tests were run in 267ms. Tests completed at 12:39:29 due to changes to GreetingResource.class.
Press [r] to re-run, [v] to view full results, [p] to pause, [h] for more options>
```

テストが成功するように文字列を戻します。その前に、保存してもテストが自動的に実行されないように一時停止しておきます。``p``キーを押下することでポーズすることができ、以下のように表示されます(最初に``mvn quarkus:dev``を実行したときと同じ状態)。

```
--
Tests paused, press [r] to resume, [w] to open the browser, [h] for more options>
```

先ほどのGreetingResource.javaの変更した文字列を元に戻し、保存します。その後、``r``キーを押下することで、継続的テストの実行モードにレジュームされ、以下の結果が得られます。

```
2021-07-28 12:44:21,685 INFO  [io.qua.test] (Test runner thread) All tests are now passing


--
All 2 tests are passing (0 skipped), 2 tests were run in 266ms. Tests completed at 12:44:21 due to changes to GreetingResource.class.
Press [r] to re-run, [v] to view full results, [p] to pause, [h] for more options>
```

テストが2件とも成功するようになっています。

このポーズ機能は、ファイルの内容を大幅に修正したり、多数のファイルを修正するときに、途中でファイルを保存した際に都度テストが実行されて毎回failとなる、といったことを防ぐために利用します。


この他のオプションについては「[QUARKUS - 継続的テスト -> 4. 継続的なテストのコントロール](https://ja.quarkus.io/guides/continuous-testing#%E7%B6%99%E7%B6%9A%E7%9A%84%E3%81%AA%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB)」に詳しく記載されています。

以上のように、継続的テスト機能を利用することで、自分で``mvn``コマンドを実行したり、IDE/VSCodeのテスト実行ボタンをクリック/ショートカット起動したりすることなく、編集したファイルを保存するだけで自動的にテストが実行され、開発作業の効率化を図ることができます。(頻繁にテストを実行することになる)テスト駆動開発も進めやすくなります。また、不要なときには一時的にポーズしたり、(当記事では試しませんでしたが)過去に壊れたテスト(failedになったテスト)のみを実行するモードもあったりと、実行を細かく制御することができ、非常に便利な機能となっています。

Quarkus 2.0を利用する際は、ぜひ継続的テストを使ってみてください。