Spring Boot 2.0.x の Web アプリを 2.1.x へバージョンアップする ( その9 )( JUnit 5 へバージョンアップ。。。@RunWith(Enclosed) + @Unroll + useJUnitPlatform() の組み合わせでテストが終わらない問題を解決する )
概要
記事一覧はこちらです。
- 今回の手順で確認できるのは以下の内容です。
参照したサイト・書籍
How to configure gradle to output total number of tests executed?
https://stackoverflow.com/questions/37173218/how-to-configure-gradle-to-output-total-number-of-tests-executedHow to use JUnit 5 with Gradle
https://medium.com/@jonashavers/how-to-use-junit-5-with-gradle-fb7c5c3286ccApache Groovyチュートリアル - 7.クロージャ
https://koji-k.github.io/groovy-tutorial/closure/index.html
目次
- 解決策
- demo プロジェクトで試してみる
- ksbysample-webapp-lending の build.gradle を変更して動作確認する
- gradle の test タスクと IntelliJ IDEA の「Run ‘All Tests’」で実行した時のテスト数が異なる原因とは?
手順
解決策
JUnit 5 のモジュールを追加したら junit-vintage-engine を依存関係に追加しないと JUnit 4 のテストを実行できないと思っていたのですが、type: Test
のタスク内で useJUnitPlatform()
を呼び出さなければ JUnit 4 か Spock のテストのみ実行される(JUnit 5 のテストが実行されない)ということに気づきました。この動作を利用します。
- test タスクとは別に testJUnit4AndSpock タスクを定義する。
- testJUnit4AndSpock タスクは
type: Test
のタスクとして定義する。 testRuntime("org.junit.vintage:junit-vintage-engine")
は依存関係に追加しない。JUnit 5 の環境下では JUnit 5 のテストのみ実行し、JUnit 4 及び Spock のテストが実行されないようにする。- test タスク内では
useJUnitPlatform()
を記述して JUnit 5 のテストのみ実行する。 - testJUnit4AndSpock タスク内では
useJUnitPlatform()
を記述せず JUnit 4 及び Spock のテストのみ実行する。
demo プロジェクトで試してみる
build.gradle を変更する
buildscript { ext { group "com.example" version "1.0.0-RELEASE" } repositories { mavenCentral() maven { url "https://repo.spring.io/release/" } maven { url "https://plugins.gradle.org/m2/" } } } plugins { id "java" id "eclipse" id "idea" id "org.springframework.boot" version "2.1.3.RELEASE" id "io.spring.dependency-management" version "1.0.6.RELEASE" id "groovy" } sourceCompatibility = 1.8 targetCompatibility = 1.8 [compileJava, compileTestGroovy, compileTestJava]*.options*.encoding = "UTF-8" [compileJava, compileTestGroovy, compileTestJava]*.options*.compilerArgs = [ "-Xlint:all,-options,-processing,-path" ] idea { module { inheritOutputDirs = false outputDir = file("$buildDir/classes/main/") } } repositories { mavenCentral() } dependencyManagement { imports { mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES) mavenBom("org.junit:junit-bom:5.4.0") } } dependencies { def spockVersion = "1.2-groovy-2.5" implementation("org.springframework.boot:spring-boot-starter-web") runtimeOnly("org.springframework.boot:spring-boot-devtools") implementation("org.apache.commons:commons-lang3") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.spockframework:spock-core:${spockVersion}") testImplementation("org.spockframework:spock-spring:${spockVersion}") // for JUnit 5 testCompile("org.junit.jupiter:junit-jupiter") testRuntime("org.junit.platform:junit-platform-launcher") } def printTestCount = { desc, result -> if (!desc.parent) { // will match the outermost suite println "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" } } task testJUnit4AndSpock(type: Test) { testLogging { events "STARTED", "PASSED", "FAILED", "SKIPPED" afterSuite printTestCount } } test.dependsOn testJUnit4AndSpock test { // for JUnit 5 useJUnitPlatform() testLogging { events "STARTED", "PASSED", "FAILED", "SKIPPED" afterSuite printTestCount } }
- dependencies block から
testRuntime("org.junit.vintage:junit-vintage-engine")
を削除します。 task testJUnit4AndSpock(type: Test) { ... }
を追加します。test.dependsOn testJUnit4AndSpock
を記述し、test タスクの前に testJUnit4AndSpock タスクが実行されるようにします。- testJUnit4AndSpock タスクでは JUnit 4 と Spock のテストだけが、test タスクでは JUnit 5 のテストだけが実行されていることを確認するために testJUnit4AndSpock タスク、test タスク内に
events "STARTED", "PASSED", "FAILED", "SKIPPED"
を記述してテストの開始や終了の状況がコンソールに出力されるようにします。 - 各タスクで実行されたテストの数をコンソールに出力したいので、How to configure gradle to output total number of tests executed? に記載されていた afterSuite closure を追加します。
Spock 以外に JUnit 4、JUnit 5 のテストクラスを作成する
src/test/java/com/example/demo/SampleUtilsJUnit4Test.java を作成し、以下の内容を記述します。org.junit.Test アノテーションが付与されているので、このテストクラスは JUnit 4 のテストクラスとして認識されます。
package com.example.demo; import org.junit.Test; import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; public class SampleUtilsJUnit4Test { @Test public void join() { assertThat(SampleUtils.join("a", "b"), is("ab")); } }
src/test/java/com/example/demo/SampleUtilsJUnit5Test.java を作成し、以下の内容を記述します。org.junit.jupiter.api.Test アノテーションが付与されているので、このテストクラスは JUnit 5 のテストクラスとして認識されます。
package com.example.demo; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class SampleUtilsJUnit5Test { @Test void join() { assertEquals("ab", SampleUtils.join("a", "b")); } }
動作確認する
src/test/groovy/com/example/demo/SampleUtilsTest.groovy は @RunWith(Enclosed) + @Unroll を組み合わせたテストにしています。
package com.example.demo import org.junit.experimental.runners.Enclosed import org.junit.runner.RunWith import spock.lang.Specification import spock.lang.Unroll @RunWith(Enclosed) class SampleUtilsTest { static class 正常処理のテスト extends Specification { @Unroll def "xxx"() { expect: SampleUtils.join(a, b) == result where: a | b || result "a" | "b" || "ab" } } }
clean タスク実行 → Rebuild Project 実行 → build タスクを実行してみると、
- タスクが全て完了し BUILD SUCCESSFUL のメッセージが出力されました。
- testJUnit4AndSpock タスクでは JUnit 4 のテストクラスである SampleUtilsJUnit4Test、Spock のテストクラスである SampleUtilsTest の2つだけが実行されています。
- test タスクでは JUnit 5 のテストクラスである SampleUtilsJUnit5Test だけが実行されています。
期待通りの動作です。これで JUnit4、Spock、JUnit 5 のモジュールを依存関係に追加した環境で、JUnit4、Spock、JUnit 5 のテストを書いてそれぞれのテストが実行できるようになりました。
ksbysample-webapp-lending の build.gradle を変更して動作確認する
build.gradle を以下のように変更します。
dependencies { .......... // for JUnit 5 // junit-jupiter で junit-jupiter-api, junit-jupiter-params, junit-jupiter-engine の3つが依存関係に追加される testCompile("org.junit.jupiter:junit-jupiter") testRuntime("org.junit.platform:junit-platform-launcher") .......... } // -ea -Dspring.profiles.active=develop -Dfile.encoding=UTF-8 -Dsun.nio.cs.map=x-windows-iso2022jp/ISO-2022-JP def jvmArgsForTask = [ "-ea", "-Dfile.encoding=UTF-8", "-Dsun.nio.cs.map=x-windows-iso2022jp/ISO-2022-JP" ] def printTestCount = { desc, result -> if (!desc.parent) { // will match the outermost suite println "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" } } bootRun { jvmArgs = jvmArgsForTask + ["-Dspring.profiles.active=develop"] } task testJUnit4AndSpock(type: Test) { jvmArgs = jvmArgsForTask + ["-Dspring.profiles.active=unittest"] testLogging { afterSuite printTestCount } } test.dependsOn testJUnit4AndSpock test { jvmArgs = jvmArgsForTask + ["-Dspring.profiles.active=unittest"] // for JUnit 5 useJUnitPlatform() testLogging { afterSuite printTestCount } }
- dependencies block から
testRuntime("org.junit.vintage:junit-vintage-engine")
を削除します。 - bootRun タスク、test タスクに個別に記述していた
jvmArgs = [ ... ]
から-Dspring.profiles.active=...
の部分以外の設定をdef jvmArgsForTask = [ ... ]
としてタスクの外側で変数として定義し、bootRun タスク、test タスクではこの変数を使用するよう変更します。 def printTestCount = { ... }
を追加します。task testJUnit4AndSpock(type: Test) { ... }
とtest.dependsOn testJUnit4AndSpock
を追加します。- test タスクに
testLogging { afterSuite printTestCount }
を追加します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行すると 無事 BUILD SUCCESSFUL のメッセージが出力されました。
Project Tool Window で src/test を選択した後コンテキストメニューを表示して「Run ‘All Tests’」を選択すると全てのテストが完了しますが、テスト数が 203 と gradle の build タスクの 141 と 62 も違います。。。なぜ?
gradle の test タスクと IntelliJ IDEA の「Run ‘All Tests’」で実行した時のテスト数が異なる原因とは?
build/reports/tests/testJUnit4AndSpock/index.html のレポートと IntelliJ IDEA の「Run ‘All Tests’」の結果を比較してみていたら、IntelliJ IDEA の方で同じテストが二重にカウントされていました。
demo プロジェクトの方でも Project Tool Window から「Run ‘All Tests’」を実行するとテストの数は 3 しかないのに 6 とカウントされます。
テストを1つずつ試すときちんと1とカウントされます。
いろいろ試してみた結果、src/test から「Run ‘All Tests’」を実行するとテスト数が 203 となりますが、
src/test/groovy/ksbysample から「Run ‘All Tests’」を実行すると 39、
src/test/java/ksbysample から「Run ‘All Tests’」を実行すると 103 で、合計が 142 となり gradle の build タスクの 141 とほぼ同じになります。テストの実行時間も約 30 秒程度短いので、src/test から実行した時には多い分のテストが実行されているようです。
「Run/Debug Configurations」の「JUnit」の設定を見てもこの原因となるような設定は見当たりませんでした。
よく分かりませんが、今後は src/test からはテストを実行せず src/test/groovy、src/test/java の下のルートパッケージから「Run ‘All Tests’」を実行することにしましょうか。。。
履歴
2019/03/01
初版発行。