1. 序章

ほとんどの場合、テストを連続して実行することは問題なく機能しますが、テストを並列化して処理を高速化することもできます。

このチュートリアルでは、JUnitとMavenのSurefireプラグインを使用してテストを並列化する方法について説明します。 まず、すべてのテストを単一のJVMプロセスで実行し、次にマルチモジュールプロジェクトで試してみます。

2. Mavenの依存関係

必要な依存関係をインポートすることから始めましょう。 Surefire2.16以降と一緒にJUnit4.7以降を使用する必要があります:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
</plugin>

一言で言えば、Surefireはテストを並行して実行する2つの方法を提供します。

  • 単一のJVMプロセス内でのマルチスレッド
  • 複数のJVMプロセスをフォークする

3. 並列テストの実行

テストを並行して実行するには、org.junit.runners.ParentRunnerを拡張するテストランナーを使用する必要があります。

ただし、デフォルトのランナーがこのクラスを拡張するため、明示的なテストランナーの動作を宣言しないテストでも機能します。

次に、並列テストの実行を示すために、それぞれがいくつかのメソッドを持つ2つのテストクラスを持つテストスイートを使用します。 実際、JUnitテストスイートの標準的な実装ならどれでもかまいません。

3.1. 並列パラメータの使用

まず、 parallel パラメーターを使用して、Surefireで並列動作を有効にしましょう。 並列処理を適用する粒度のレベルを示します。

可能な値は次のとおりです。

  • メソッド–は別々のスレッドでテストメソッドを実行します
  • クラス–は別々のスレッドでテストクラスを実行します
  • classesAndMethods –クラスとメソッドを別々のスレッドで実行します
  • スイート–スイートを並行して実行します
  • suitesAndClasses –スイートとクラスを別々のスレッドで実行します
  • suitesAndMethods –はクラスとメソッドに別々のスレッドを作成します
  • all – は、スイート、クラス、およびメソッドを別々のスレッドで実行します

この例では、allを使用します。

<configuration>
    <parallel>all</parallel>
</configuration>

次に、Surefireで作成するスレッドの総数を定義しましょう。 これは2つの方法で実行できます。

Surefireが作成するスレッドの最大数を定義するthreadCountを使用します。

<threadCount>10</threadCount>

または、 useUnlimitedThreads パラメーターを使用して、CPUコアごとに1つのスレッドを作成します。

<useUnlimitedThreads>true</useUnlimitedThreads>

デフォルトでは、threadCountはCPUコアごとです。 パラメータperCoreThreadCountを使用して、この動作を有効または無効にできます。

<perCoreThreadCount>true</perCoreThreadCount>

3.2. スレッド数制限の使用

ここで、メソッド、クラス、およびスイートのレベルで作成するスレッドの数を定義するとします。 これは、threadCountMethods、threadCountClasses、およびthreadCountSuitesパラメーターを使用して実行できます。

これらのパラメーターを、前の構成の threadCount と組み合わせてみましょう:

<threadCountSuites>2</threadCountSuites>
<threadCountClasses>2</threadCountClasses>
<threadCountMethods>6</threadCountMethods>

すべての並列で使用したため、メソッド、スイート、およびクラスのスレッド数を定義しました。 ただし、leafパラメーターの定義は必須ではありません。 Surefireは、リーフパラメータが省略された場合に使用するスレッドの数を推測します。

たとえば、 threadCountMethods を省略した場合は、 threadCount > threadCountClasses +threadCountSuites。を確認する必要があります。

無制限の数のスレッドを使用している場合でも、クラス、スイート、またはメソッド用に作成されるスレッドの数を制限したい場合があります。

このような場合にも、スレッド数の制限を適用できます。

<useUnlimitedThreads>true</useUnlimitedThreads>
<threadCountClasses>2</threadCountClasses>

3.3. タイムアウトの設定

テストの実行に時間制限があることを確認する必要がある場合があります。

これを行うには、parallelTestTimeoutForcedInSecondsパラメーターを使用できます。これにより、現在実行中のスレッドが中断され、タイムアウトが経過した後、キューに入れられたスレッドは実行されません。

<parallelTestTimeoutForcedInSeconds>5</parallelTestTimeoutForcedInSeconds>

もう1つのオプションは、parallelTestTimeoutInSecondsを使用することです。

この場合、キューに入れられたスレッドのみが実行を停止されます。

<parallelTestTimeoutInSeconds>3.5</parallelTestTimeoutInSeconds>

それでも、どちらのオプションでも、タイムアウトが経過すると、テストはエラーメッセージで終了します。

3.4. 警告

Surefireは、親スレッドで @Parameters @BeforeClass 、および@AfterClassで注釈が付けられた静的メソッドを呼び出します。 したがって、テストを並行して実行する前に、潜在的なメモリの不整合や競合状態を確認してください。

また、共有状態を変更するテストは、並行して実行するのに適した候補ではありません。

4. マルチモジュールMavenプロジェクトでのテスト実行

これまで、Mavenモジュール内でテストを並行して実行することに重点を置いてきました。

しかし、Mavenプロジェクトに複数のモジュールがあるとしましょう。 これらのモジュールは順番に作成されるため、各モジュールのテストも順番に実行されます。

モジュールを並列にビルドするMavenの-Tパラメーターを使用して、このデフォルトの動作を変更できます。 これは2つの方法で行うことができます。

プロジェクトのビルド中に使用するスレッドの正確な数を指定することができます。

mvn -T 4 surefire:test

または、ポータブルバージョンを使用して、CPUコアごとに作成するスレッドの数を指定します。

mvn -T 1C surefire:test

いずれにせよ、テストを高速化するだけでなく、実行時間を構築することもできます。

5. JVMのフォーク

parallelオプションを介した並列テストの実行では、スレッドを使用してJVMプロセス内で同時実行が発生します。

スレッドは同じメモリスペースを共有しているため、これはメモリと速度の点で効率的です。 ただし、予期しない競合状態やその他の微妙な同時実行関連のテストの失敗が発生する可能性があります。 結局のところ、同じメモリスペースを共有することは、祝福と呪いの両方になる可能性があります。

スレッドレベルの同時実行性の問題を防ぐために、Surefireは別の並列テスト実行モードを提供します:フォークとプロセスレベルの同時実行性。 フォークされたプロセスの考え方は、実際には非常に単純です。 複数のスレッドを生成してそれらの間でテストメソッドを配布する代わりに、surefireは新しいプロセスを作成し、同じ配布を行います。

異なるプロセス間で共有メモリがないため、これらの微妙な同時実行性のバグに悩まされることはありません。 もちろん、これにはメモリ使用量が増え、速度が少し低下します。

とにかく、フォークを有効にするには、forkCountプロパティを使用して、任意の正の値に設定する必要があります:

<forkCount>3</forkCount>

ここで、surefireはJVMから最大3つのフォークを作成し、それらでテストを実行します。 forkCount のデフォルト値は1です。これは、 maven-surefire-plugin が1つの新しいJVMプロセスを作成して、1つのMavenモジュールですべてのテストを実行することを意味します。

forkCount プロパティは、-Tと同じ構文をサポートします。 つまり、 C を値に追加すると、その値にシステムで使用可能なCPUコアの数が乗算されます。 例えば:

<forkCount>2.5C</forkCount>

次に、2コアのマシンで、Surefireは並列テスト実行用に最大5つのフォークを作成できます。

デフォルトでは、Surefireは作成されたフォークを他のテストに再利用します。 ただし、reuseForksプロパティをfalseに設定すると、1つのテストクラスを実行した後に各フォークが破棄されます。

また、フォークを無効にするために、forkCountをゼロに設定することができます。

6. 結論

要約すると、マルチスレッド動作を有効にし、parallelパラメーターを使用して並列度を定義することから始めました。 その後、Surefireが作成するスレッドの数に制限を適用しました。 後で、タイムアウトパラメータを設定して、テストの実行時間を制御します。

最後に、ビルドの実行時間を短縮し、マルチモジュールのMavenプロジェクトで実行時間をテストする方法を検討しました。

いつものように、ここに示されているコードはGitHubで入手できます。