18. コンテナテストフレームワーク

18.1. MicroShed Testing

ここでは、MicroShed Testing を利用した WebOTX コンテナのテスト方法について、例を用いて説明します。

MicroShed Testing は、マイクロサービスアプリケーションの統合テストを簡単かつ素早く行うための Java ライブラリです。 このライブラリを使用することで、作成した Web アプリケーションを WebOTX のコンテナに配備し、データベースなどの他のコンテナと連携した環境上で自動的にテストを実施することができます。テストケースは Java で記述します。

例えば、MicroShed Testing が公開している以下のサンプルアプリケーションを WebOTX のコンテナ上で動作させる手順を用いて、説明します。

maven-app.zip

※このサンプルアプリケーションは MicroShed Testing で公開されているアプリケーション(maven-app)を Java EE 7 用に変更したものです。

18.1.1. サンプルアプリケーションのテスト実施手順

サンプルアプリケーション maven-app.zip を解凍してください。
解凍した先に以下の Dockerfile が展開されます。

Dockerfile:

FROM webotx/webotx-express-ubi7:10.40.00.00

COPY target/myservice.war /opt/WebOTX/domains/domain1/autodeploy/

EXPOSE 8080
Dockerfile の内容

FROM で指定している Docker のイメージは WebOTX Uber JAR や WebOTX Standard 用の Docker イメージに置き換えることもできます。
COPY では、ビルドしたアプリケーションを配備するため、ドメインディレクトリ配下の autodeploy に配置しています。ここでコピーするファイル名は、アプリケーションによって変更する必要があります。
EXPOSE では、MicroShed Testing で使用する HTTP ポート番号を指定しています。 MicroShed Testing では、ここで EXPOSE されたポートを検索して、見つかったポートに対してポートフォワードが設定されます。このポートに対して、テスト実行時にホスト側からリクエストが行われます。 もし、EXPOSE を指定しなかった場合は、テスト実施時にポート番号が取得できず、テストに失敗するので注意してください。

READY 状態となるまでの待ち合わせ

MicroShed Testing は、アプリケーションが READY 状態となるまで待ち合わせてからテストを実施する必要があります。この待ち合わせを行うため、サンプルアプリケーションでは以下のように .withReadinessPath にて、/myservice/people から 200 応答が返ってくるまで待ち合わせるといった定義をしています。アプリケーションによって、どのようなリクエストで待ち合わせるか設定を変更してください。

src/test/java/org/example/app/it/JaxrsJsonIT.java(一部抜粋):

    @Container
    public static ApplicationContainer app = new ApplicationContainer()
                    .withAppContextRoot("/myservice")
                    .withReadinessPath("/myservice/people");
Maven の実行

次のように Maven のコマンドを実行することで、Docker コンテナが起動してアプリケーションが配備された後、テストが実行されます。

このとき、Docker コンテナが起動するまでの待ち合わせ時間が、MicroShed の仕様でデフォルト 30 秒となっており、環境によっては Timeout が発生します。 もし、Timeout が発生する場合は、以下のように環境変数で CI=true を指定して、Docker コンテナが起動するまでの待ち合わせ時間を 90 秒とします。

Linux の場合:

CI=true mvn clean install

Windows の場合:

set CI=true
mvn clean install
実行結果

テストの実行結果は次のように出力され、全てテストが成功した場合は、Failures、Errors が 0 となります。

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.example.app.it.JaxrsJsonIT
0 DEBUG org.microshed.testing.ApplicationEnvironment  - Found ApplicationEnvironment class org.microshed.testing.testcontainers.config.TestcontainersConfiguration with priority=-30, available=true
4 DEBUG org.microshed.testing.ApplicationEnvironment  - Found ApplicationEnvironment class org.microshed.testing.ManuallyStartedConfiguration with priority=-10, available=false
4 DEBUG org.microshed.testing.ApplicationEnvironment  - Found ApplicationEnvironment class org.microshed.testing.testcontainers.config.HollowTestcontainersConfiguration with priority=-20, available=false
6 INFO org.microshed.testing.jupiter.MicroShedTestExtension  - Using ApplicationEnvironment class: org.microshed.testing.testcontainers.config.TestcontainersConfiguration
5894 INFO org.microshed.testing.testcontainers.ApplicationContainer  - Found exposed ports: [8080/tcp]
5894 INFO org.microshed.testing.testcontainers.ApplicationContainer  - Automatically selecting default HTTP port: 8080
5895 INFO org.microshed.testing.testcontainers.ApplicationContainer  - Using ServerAdapter: org.microshed.testing.testcontainers.ApplicationContainer.DefaultServerAdapter
5903 DEBUG org.microshed.testing.testcontainers.config.TestcontainersConfiguration  - No networks explicitly defined. Using shared network for all containers in class org.example.app.it.JaxrsJsonIT
     :
 (省略)
     :
9372 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT: Starting Domain domain1, please wait.
9380 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT: Redirecting output to /opt/WebOTX/domains/domain1/logs/server.log
9826 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT: Edition is Express
43370 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT: Domain domain1 is ready to receive client requests. Additional services are being started in background.
43410 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT: Domain [domain1] is running [Version 10.40.00.00 (build xxxxxxxx)].
43411 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT:
43411 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT:     * Standard JMX Clients (like JConsole) can connect to JMXServiceURL:
43411 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT:         [service:jmx:rmi:///jndi/rmi://xxxxxxxxxx:6212/jmxrmi] for domain management purposes.
43411 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT:
43412 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT:     * Domain listens on at least following ports for connections:
43412 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT:         [5858 6212 6712 8080 8443].
43412 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT:
43416 INFO org.microshed.testing.testcontainers.ApplicationContainer  - STDOUT: Command start-domain executed successfully.
     :
 (省略)
     :
61758 INFO org.microshed.testing.jaxrs.JsonBProvider  - Response from server: 4618523015471329430
61798 INFO org.microshed.testing.jaxrs.JsonBProvider  - Response from server: {"id":4618523015471329430,"name":"Newborn","age":0}
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 61.81 s - in org.example.app.it.JaxrsJsonIT
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0

18.1.2. 既存アプリケーションへの追加方法

既存のアプリケーションに対して、MicroShed Testing を使用するためには、Maven の依存モジュールに以下を追加します。

<dependencies>
    <dependency>
        <groupId>org.microshed</groupId>
        <artifactId>microshed-testing-testcontainers</artifactId>
        <version>0.9.1</version>
       <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.4.2</version>
        <scope>test</scope>
    </dependency>

    <!-- other dependencies... -->
</dependencies>

MicroShed Testing のテストケースとして、クラスまたはメソッドに @MicroShedTest アノテーションを付与して定義します。

@MicroShedTest
public class MyTestA {
    @Container
    public static ApplicationContainer app = new ApplicationContainer()
                    .withAppContextRoot("/myservice");
    // ...
}

データベースなど複数のコンテナと連携してテストを実施したい場合は、次のように SharedContainerConfiguration.startContainers() を Override して、コンテナを起動します。

public class AppContainerConfig implements SharedContainerConfiguration {

    @Container
    public static GenericContainer<?> mongo = new GenericContainer<>("mongo:3.4")
                    // ...

    @Container
    public static ApplicationContainer app = new ApplicationContainer()
                    // ...

    @Override
    public void startContainers() {
        mongo.start();
        app.start();
    }
}

@MicroShedTest
@SharedContainerConfig(AppContainerConfig.class)
public class MyTestA {
    // ...
}

@MicroShedTest
@SharedContainerConfig(AppContainerConfig.class)
public class MyTestB {
    // ...
}

MicroShed Testing の詳細な使い方については、以下の MicrShed の公式ページをご参照ください。

MicroShed Testing

18.2. Testcontainers

ここでは、Testcontainers を利用した WebOTX コンテナのテスト方法について、例を用いて説明します。

Testcontainersは、Javaテストケースに組み込むことで、テストケースごとにデータベースなどの他のコンテナを起動し、アプリケーション・コンテナ間での結合評価を行うことができるOSSです。

Testcontainersには以下のような利点があります。

18.2.1. Testcontainersの利用

Testcontainersの利用法について、記載します。
18.2.1.1. 前提条件

Testcontainersの利用には、テスト実行環境にDockerがインストールされている必要があります。

18.2.1.2. ライブラリの入手

Testcontainersのライブラリは、mvnrepository.comで公開されています。

https://mvnrepository.com/artifact/org.testcontainers/testcontainers

Mavenによりアプリケーションを作成している場合は、アプリケーションのpom.xmlに以下のように依存関係を追加して下さい。

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>Testcontainersのバージョン</version>
    <scope>test</scope>
</dependency>
18.2.1.3. テストコードのサンプル

以下にテストコードのサンプルを記載します。

テストケース実施前にPostgresのコンテナを起動し、テストケース内で接続しています。

public class SampleJunitTestPostgres {
    public static final Logger LOGGER = LogManager.getLogger(SampleJunitTestPostgres.class);

    Connection conn = null;                                            
    Statement stmt = null;
    @ClassRule
    public static GenericContainer POSTGRES = //コンテナを生成し、

        new GenericContainer("postgres:12.3") // 開けるポートや環境変数を設定
        .withExposedPorts(5432)
        .withEnv("POSTGRES_PASSWORD", "postgrespw")
        .withEnv("POSTGRES_USER", "postgresuser")
        .withEnv("POSTGRES_DB", "postgresdb");

    @Test
    public void simplePostgres1() {

        Boolean isSuccess = false;
        try {
            Thread.sleep(10000); // コンテナ起動を10秒間待ち合わせ
            // 生成したPostgresコンテナへ接続
            Class.forName("org.postgresql.Driver");
            String url = "jdbc:postgresql://"
                + POSTGRES.getContainerIpAddress()
                + ":" + POSTGRES.getMappedPort(5432)
                + "/" + "postgresdb";

            conn = DriverManager.getConnection(url, "postgresuser", "postgrespw");
            isSuccess = true; // Connectでエラーが出なければ成功とする

        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
                conn = null;
            }
        }// 接続して例外が出なかったらテスト成功
        assertThat(isSuccess).isEqualTo(true);
    }
}

Testcontainers の詳細な使い方については、以下の Testcontainers の公式ページをご参照ください。

Testcontainers