これは、なにをしたくて書いたもの?
前に、こんなエントリーを書きました。
Maven Surefire Pluginのincludeになにを指定したらいいのかわからないという話 - CLOVER🍀
この時はMaven Surefire Pluginのinclues
/include
、excludes
/exclude
になにを指定すればよいのかを確認してみました。
この指定をファイルで行う方法があるようなので、今回はこちらを試してみます。
Maven Surefire PluginのincludesFileとexcludesFile
inclues
/include
、excludes
/exclude
自体については、こちらに書かれています。
Maven Surefire Plugin – Inclusions and Exclusions of Tests
また、なにを指定すればよいのかはこちらで確認してみました。
Maven Surefire Pluginのincludeになにを指定したらいいのかわからないという話 - CLOVER🍀
こちらのエントリーを書いていた時、これらの指定をファイルで行えることに気づいていたので今回はこちらを試してみようかなという話です。
書き方自体はinclues
/include
、excludes
/exclude
と同じで、Maven Surefire Plugin 3.0.0-M6以降はテストメソッドの指定もできるようです。
空行や#
で始まる行は無視され、すでにincludes
やexcludes
を指定している場合はファイルで指定したパターンが追加されるようです。
A file containing include patterns. Blank lines, or lines starting with # are ignored. If includes are also specified, these patterns are appended. Example with path, simple and regex includes:
A file containing exclude patterns. Blank lines, or lines starting with # are ignored. If excludes are also specified, these patterns are appended. Example with path, simple and regex excludes:
今回はincludesFile
を使ってみます。
なにをしたいか?
テストコードが増えていくと、テストの実行速度が問題になることがあります。
テストの実行自体を速くするのはもちろん必要ですが、テストを分割して並列実行というアプローチもあると思うので、そちらに
利用できないかなと思いまして。
というわけで、適当にいくつかテスト対象のコードとテストコードを書き、includesFile
を使ってテストクラス群を分割して実行して
みたいと思います。
環境
今回の環境はこちら。
$ java --version openjdk 21.0.4 2024-07-16 OpenJDK Runtime Environment (build 21.0.4+7-Ubuntu-1ubuntu222.04) OpenJDK 64-Bit Server VM (build 21.0.4+7-Ubuntu-1ubuntu222.04, mixed mode, sharing) $ mvn --version Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 21.0.4, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.15.0-124-generic", arch: "amd64", family: "unix"
テスト対象の準備
まずはテスト対象のプログラムを作成していきます。
Mavenの設定など。
<properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.11.3</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.5.1</version> </plugin> </plugins> </build> </project>
テスト対象のクラス。
src/main/java/org/littlewings/surefire/CalcService1.java
package org.littlewings.surefire; public class CalcService1 { public int plus(int a, int b) { return a + b; } public int minus(int a, int b) { return a - b; } }
テストクラス。
src/test/java/org/littlewings/surefire/CalcService1Test.java
package org.littlewings.surefire; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class CalcService1Test { @Test void plus() { CalcService1 sut = new CalcService1(); assertEquals(5, sut.plus(3, 2)); } @Test void minus() { CalcService1 sut = new CalcService1(); assertEquals(2, sut.minus(8, 6)); } }
今回は内容ではなく数に着目するので、ほぼ同じ内容でクラスを増やします。
$ tree src src ├── main │ ├── java │ │ └── org │ │ └── littlewings │ │ └── surefire │ │ ├── CalcService1.java │ │ ├── CalcService10.java │ │ ├── CalcService2.java │ │ ├── CalcService3.java │ │ ├── CalcService4.java │ │ ├── CalcService5.java │ │ ├── CalcService6.java │ │ ├── CalcService7.java │ │ ├── CalcService8.java │ │ └── CalcService9.java │ └── resources └── test └── java └── org └── littlewings └── surefire ├── CalcService10Test.java ├── CalcService1Test.java ├── CalcService2Test.java ├── CalcService3Test.java ├── CalcService4Test.java ├── CalcService5Test.java ├── CalcService6Test.java ├── CalcService7Test.java ├── CalcService8Test.java └── CalcService9Test.java 11 directories, 20 files
増やした分は同じ数字のテスト対象クラス、テストクラスがペアになっています。
src/test/java/org/littlewings/surefire/CalcService2Test.java
package org.littlewings.surefire; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class CalcService2Test { @Test void plus() { CalcService2 sut = new CalcService2(); assertEquals(5, sut.plus(3, 2)); } @Test void minus() { CalcService2 sut = new CalcService2(); assertEquals(2, sut.minus(8, 6)); } }
テストを実行。
$ mvn test
全部のテストが実行されます。
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.littlewings.surefire.CalcService3Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.053 s -- in org.littlewings.surefire.CalcService3Test [INFO] Running org.littlewings.surefire.CalcService9Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.006 s -- in org.littlewings.surefire.CalcService9Test [INFO] Running org.littlewings.surefire.CalcService5Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.007 s -- in org.littlewings.surefire.CalcService5Test [INFO] Running org.littlewings.surefire.CalcService2Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.006 s -- in org.littlewings.surefire.CalcService2Test [INFO] Running org.littlewings.surefire.CalcService7Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.006 s -- in org.littlewings.surefire.CalcService7Test [INFO] Running org.littlewings.surefire.CalcService4Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 s -- in org.littlewings.surefire.CalcService4Test [INFO] Running org.littlewings.surefire.CalcService8Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.006 s -- in org.littlewings.surefire.CalcService8Test [INFO] Running org.littlewings.surefire.CalcService1Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 s -- in org.littlewings.surefire.CalcService1Test [INFO] Running org.littlewings.surefire.CalcService6Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 s -- in org.littlewings.surefire.CalcService6Test [INFO] Running org.littlewings.surefire.CalcService10Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.006 s -- in org.littlewings.surefire.CalcService10Test [INFO] [INFO] Results: [INFO] [INFO] Tests run: 20, Failures: 0, Errors: 0, Skipped: 0 [INFO]
実行するテストクラスをファイルに分割する
では、includesFile
に指定するテストクラスを振り分けていきます。
テストクラスはこれだけあります。
$ find src/test/java -name '*.java' src/test/java/org/littlewings/surefire/CalcService1Test.java src/test/java/org/littlewings/surefire/CalcService5Test.java src/test/java/org/littlewings/surefire/CalcService7Test.java src/test/java/org/littlewings/surefire/CalcService2Test.java src/test/java/org/littlewings/surefire/CalcService3Test.java src/test/java/org/littlewings/surefire/CalcService6Test.java src/test/java/org/littlewings/surefire/CalcService10Test.java src/test/java/org/littlewings/surefire/CalcService4Test.java src/test/java/org/littlewings/surefire/CalcService9Test.java src/test/java/org/littlewings/surefire/CalcService8Test.java
incluesFile
に指定するにはsrc/test/java/
の部分があると困るので、切り取ります。
$ find src/test/java -name '*.java' | perl -wp -e 's!src/test/java/!!' org/littlewings/surefire/CalcService1Test.java org/littlewings/surefire/CalcService5Test.java org/littlewings/surefire/CalcService7Test.java org/littlewings/surefire/CalcService2Test.java org/littlewings/surefire/CalcService3Test.java org/littlewings/surefire/CalcService6Test.java org/littlewings/surefire/CalcService10Test.java org/littlewings/surefire/CalcService4Test.java org/littlewings/surefire/CalcService9Test.java org/littlewings/surefire/CalcService8Test.java
これをファイルに書き出します。
$ find src/test/java -name '*.java' | perl -wp -e 's!src/test/java/!!' > test_classes.txt
テストクラスはクラス数で分割するよりもグループ数で分割することの方が多いような気がするので、数で割ってみましょう。
今回は3グループに分けます。
$ split -n 'l/3' -d test_classes.txt test_classes_group_
最後に指定しているのは分割後のファイルのprefixで、-d
オプションを指定することで分割後のファイルのsuffixを数字(0始まり)にできます。
-n
で指定のファイル数に分割できますが、l/N
の形式にしないと行の途中で切られたりします。
-n, --number=CHUNKS 作成する出力ファイル数を CHUNKS 個にする。下記の説明を参照 塊 (CHUNKS) には以下を指定できます: N 入力サイズに基づいて N 個のファイルに分割する K/N N 個中 K 番目を標準出力に出力する l/N N 個のファイルに分割すが、行やレコード内の分割は行わない l/K/N N 個中 K 番目を標準出力に出力するが、行やレコード内の分割は行わない r/N 'l' と同様だがラウンドロビン分割をする r/K/N 上記と同様だが N 個中 K 番目を標準出力に出力する
作成されたファイル。
$ ll test_classes_group_* -rw-rw-r-- 1 xxxxx xxxxx 188 10月 26 16:35 test_classes_group_00 -rw-rw-r-- 1 xxxxx xxxxx 142 10月 26 16:35 test_classes_group_01 -rw-rw-r-- 1 xxxxx xxxxx 141 10月 26 16:35 test_classes_group_02
中身。
test_classes_group_00
org/littlewings/surefire/CalcService1Test.java org/littlewings/surefire/CalcService5Test.java org/littlewings/surefire/CalcService7Test.java org/littlewings/surefire/CalcService2Test.java
test_classes_group_01
org/littlewings/surefire/CalcService3Test.java org/littlewings/surefire/CalcService6Test.java org/littlewings/surefire/CalcService10Test.java
test_classes_group_02
org/littlewings/surefire/CalcService4Test.java org/littlewings/surefire/CalcService9Test.java org/littlewings/surefire/CalcService8Test.java
あとは実行するだけです。
0番目。
$ mvn test -Dsurefire.includesFile=test_classes_group_00
結果。
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.littlewings.surefire.CalcService5Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.064 s -- in org.littlewings.surefire.CalcService5Test [INFO] Running org.littlewings.surefire.CalcService2Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.007 s -- in org.littlewings.surefire.CalcService2Test [INFO] Running org.littlewings.surefire.CalcService7Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.007 s -- in org.littlewings.surefire.CalcService7Test [INFO] Running org.littlewings.surefire.CalcService1Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.008 s -- in org.littlewings.surefire.CalcService1Test [INFO] [INFO] Results: [INFO] [INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0 [INFO]
1番目。
$ mvn test -Dsurefire.includesFile=test_classes_group_01
結果。
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.littlewings.surefire.CalcService3Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.089 s -- in org.littlewings.surefire.CalcService3Test [INFO] Running org.littlewings.surefire.CalcService6Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.010 s -- in org.littlewings.surefire.CalcService6Test [INFO] Running org.littlewings.surefire.CalcService10Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.023 s -- in org.littlewings.surefire.CalcService10Test [INFO] [INFO] Results: [INFO] [INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 [INFO]
2番目。
$ mvn test -Dsurefire.includesFile=test_classes_group_02
結果。
[INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.littlewings.surefire.CalcService9Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.066 s -- in org.littlewings.surefire.CalcService9Test [INFO] Running org.littlewings.surefire.CalcService4Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.008 s -- in org.littlewings.surefire.CalcService4Test [INFO] Running org.littlewings.surefire.CalcService8Test [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.007 s -- in org.littlewings.surefire.CalcService8Test [INFO] [INFO] Results: [INFO] [INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 [INFO]
よいのではないでしょうか。
おわりに
Maven Surefire Pluginで、includesFile
を使ってテストクラスを分割してファイルで指定、実行してみました。
あまり頻繁に使うものではないと思いますが、テストの実行でひと工夫したい時などに覚えておくとよいかなと思います。