Gradle を 9 に上げると、テストが 1 件も起動せずにビルドが止まります。
Execution failed for task ':test'.
> Test process encountered an unexpected problem.
> Could not start Gradle Test Executor 1: Failed to load JUnit Platform. Please ensure that the JUnit Platform is available on the test runtime classpath.
Gradle 9.1 では文言が違います。原因も修正も同じです。
> Could not start Gradle Test Executor 1: Failed to load JUnit Platform. Please ensure that all JUnit Platform dependencies are available on the test's runtime classpath, including the JUnit Platform launcher.
Gradle 8 までは、junit-platform-launcher をテストの実行時クラスパスへ暗黙に注入していました。Gradle 9.0 でその注入が削除されました。 修正は build.gradle の dependencies に testRuntimeOnly 'org.junit.platform:junit-platform-launcher' の 1 行を足すことです。足す行に版は書きません(理由は後述)。
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.4'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' // この 1 行を足す
}
同じ原因が別の症状で出ることもあります。Gradle 8 のまま JUnit の版を上げた場合は、次の形になります(後述)。
> There were failing tests. See the report at: ...
org.junit.platform.commons.JUnitException: TestEngine with ID 'junit-jupiter' failed to discover tests
なぜ Gradle 9 で出るのか:暗黙注入が削除された
JUnit 5 は 3 つの層に分かれています。テストを書く API(junit-jupiter-api)、それを実行するエンジン(junit-jupiter-engine)、そしてビルドツールがエンジンを起動するための launcher(junit-platform-launcher)です。junit-jupiter を 1 つ書けば前の 2 つは入りますが、launcher は入りません。解決結果を見ると分かります。
$ gradle dependencies --configuration testRuntimeClasspath
testRuntimeClasspath - Runtime classpath of source set 'test'.
\--- org.junit.jupiter:junit-jupiter:5.11.4
+--- org.junit.jupiter:junit-jupiter-api:5.11.4
+--- org.junit.jupiter:junit-jupiter-params:5.11.4
\--- org.junit.jupiter:junit-jupiter-engine:5.11.4
\--- org.junit.platform:junit-platform-engine:1.11.4
junit-platform-engine は来ていますが、junit-platform-launcher は 1 行もありません(この出力での出現回数は 0 です)。engine は「テストを実行する側」、launcher は「engine を探して起動する側」で、別の成果物です。
Gradle 8 までは、launcher が宣言されていない場合に Gradle が自分の配布物に同梱しているものを実行時クラスパスへ流し込んでいました。そのため testRuntimeOnly を書かなくてもテストが動いていました。
この注入は Gradle 8 の時点で非推奨になっていて、警告が出ます。ただし既定では要約しか出ません。
$ gradle test --no-daemon --console=plain
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
--warning-mode all を付けると、名指しの警告が出ます。
$ gradle test --no-daemon --console=plain --warning-mode all
The automatic loading of test framework implementation dependencies has been deprecated. This is scheduled to be removed in Gradle 9.0. Declare the desired test framework directly on the test suite or explicitly declare the test framework implementation dependencies on the test's runtime classpath.
Gradle 9.0 で予告どおり削除され、launcher が実行時クラスパスに無いプロジェクトはテストの起動そのものに失敗します。エラーはテストの中身ではなく、テストを起動する側で出ます。 Gradle Test Executor 1 はテストランナーのプロセスで、そこが起動できていません。
Gradle 8 のままでも、JUnit を上げれば同じ原因で止まります
境界は Gradle の版だけではありません。同じプロジェクト(launcher の宣言なし)で junit-jupiter の版だけを変え、Gradle は 8.14.3 に固定して測るとこうなります。
| junit-jupiter | 結果 |
|---|---|
| 5.10.2 | 通る |
| 5.11.4 | 通る |
| 5.12.0 | 失敗 |
| 5.12.1 | 失敗 |
| 5.12.2 | 失敗 |
| 5.13.4 | 失敗 |
境界は 5.11.4 と 5.12.0 のあいだです。5.12.0 の時点で既に落ちます。 同じ症状は gradle:8.10.2-jdk21 でも出ました(junit-jupiter 5.12.2)ので、8.14.3 に固有の話ではありません(8.0〜8.9 は測っていません)。
注入される launcher は、ビルドスクリプトで指定した junit-jupiter の版と連動しません。Gradle 8.14.3 を固定したまま junit-jupiter だけを上げると、ある版から起動できなくなる——これが上の表です。エンジンだけが新しくなり、launcher が置いていかれた状態です。
(注入された launcher の版そのものは観測できていません。注入先は test タスクの classpath ではなくテスト実行プロセス側で、doFirst から classpath.files を覗いても現れませんでした。上の表は、版が連動しないことを成否の形で示しています。)
このときの見え方が Gradle 9 と違います。ビルドの結論は「テストが失敗した」です。
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: ...
テストレポートの XML を開くと、中身は起動の失敗です。
Caused by: org.junit.platform.commons.JUnitException: TestEngine with ID 'junit-jupiter' failed to discover tests
build/test-results/test/*.xml の testcase の名前は failed to execute tests で、自分が書いたテストメソッド名ではありません。失敗として数えられているのは 1 件で、それはテストではなく実行器です。
修正は Gradle 9 のときと同じ 1 行です。つまり境界は 2 本あって、どちらも「launcher を宣言していない」という同じ状態から出ています。
- Gradle 9.0 以降:JUnit の版によらず
Failed to load JUnit Platform - Gradle 8 系で
junit-jupiterを 5.12 以降にした場合:TestEngine with ID 'junit-jupiter' failed to discover tests
Gradle 8 に留まっても、JUnit を 5.12.0 以上に上げた時点でこちらを先に踏みます(Gradle 8.14.3 で 5.12.0 / 5.12.1 / 5.12.2 / 5.13.4、Gradle 8.10.2 で 5.12.2 を実測)。
直し方:launcher を実行時クラスパスへ宣言する
再現に使った最小構成の build.gradle はこの形です。足すのは testRuntimeOnly の 1 行だけで、ほかの行は変えません。
plugins { id 'java' }
repositories { mavenCentral() }
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.4'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' // この 1 行を足す
}
test { useJUnitPlatform() }
版は書きません。junit-jupiter が junit-bom を引き、bom が launcher の版を決めるからです。上の構成で解決結果を見ると、宣言に版が無い launcher が engine と同じ版になります。
$ gradle dependencies --configuration testRuntimeClasspath
+--- org.junit.jupiter:junit-jupiter:5.11.4
| +--- org.junit:junit-bom:5.11.4
| | +--- org.junit.platform:junit-platform-launcher:1.11.4 (c)
| | \--- org.junit.platform:junit-platform-engine:1.11.4 (c)
\--- org.junit.platform:junit-platform-launcher -> 1.11.4
junit-jupiter を上げれば launcher も一緒に上がるので、前節の版ずれもここで閉じます。この 1 行だけで Gradle 9.0.0 が終了コード 0 になることを実測しています。
Kotlin DSL(build.gradle.kts)なら、足す行はこの表記になります(実測は Groovy DSL で行っています)。
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.11.4")
testRuntimeOnly("org.junit.platform:junit-platform-launcher") // この 1 行を足す
}
Test Suites DSL(testing { suites { ... } })を使っている場合は、useJUnitJupiter() を宣言すると Gradle が必要な依存を組み立てます。上の非推奨警告が言う「Declare the desired test framework directly on the test suite」がこの経路です(当プロジェクトでは Test Suites DSL の形は実測していません)。
切り分け
There were failing testsと出るのに、テストコードに問題が見つからない:build/test-results/test/*.xmlを開いてください。TestEngine with ID 'junit-jupiter' failed to discover testsがあれば、テストの失敗ではなく起動の失敗です。上の 1 行を足してください。- Gradle 9.0 と 9.1 でメッセージが違う:共通する部分は
Could not start Gradle Test Executor 1: Failed to load JUnit Platform.までです(Platform.の直後は空白が 2 つ)。9.1 はincluding the JUnit Platform launcherが続きます。原因も修正も同じです。 - マルチプロジェクトで一部だけ落ちる:
testRuntimeOnlyを足したサブプロジェクトだけが直ります。ルートのsubprojects {}や共通の convention plugin へ入れてください。 - Spring Boot を使っている:
spring-boot-starter-testは launcher を運びません。ただし**org.springframework.bootの Gradle プラグインを当てているモジュールでは、プラグイン側が足します**(spring-projects/spring-boot#46037はこれを理由に declined。starter へ入れない理由は「Maven 利用者に無関係な依存が載るから」)。プラグインを当てないサブプロジェクト(実行可能 JAR を作らないライブラリモジュールなど)だけが、testRuntimeOnlyを自分で宣言するか Test Suites を使うことになります。同 issue で Spring Boot のメンテナがそう案内しています(当プロジェクトでは Spring Boot 構成を実測していません)。 - Maven で同じことが起きないか:surefire は launcher が無いときに自分で足す実装を持ちます(同 issue で
AbstractSurefireMojoの該当箇所が挙げられています)。Gradle 9 の暗黙注入の削除は Gradle 側の変更で、Maven には当たりません。ただし surefire の版や provider の構成まで当プロジェクトで実測してはいないので、Maven 側で JUnit Platform の起動に失敗したときは launcher と provider の依存を自分で確かめてください。 - Android で Gradle の版を選んでいない:Android Gradle Plugin が要求する Gradle の版に引き上げられます。AGP を 9 系へ上げると Gradle 9 が要求されるので、この境界は AGP の更新と同時に来ます(バージョンの対応表は Android の公式ドキュメントに拠ります)。
Unsupported class file major versionが出ている:シグネチャが違えば別の境界です。こちらはFailed to load JUnit Platformでテスト実行プロセスの起動が止まり、あちらは JDK の版が合わずにビルドスクリプトの解析が止まります。Gradle が Java の版で止まる場合 を見てください。
検証環境
gradle@sha256:8894a410...(gradle:9.0.0-jdk21/ JDK 21)、junit-jupiter:5.11.4、ネットワーク有り(Maven Central)- 再現:
gradle testがCould not start Gradle Test Executor 1: Failed to load JUnit Platform.を出して終了コード 1 - 修正:
build.gradleにtestRuntimeOnly 'org.junit.platform:junit-platform-launcher'を 1 行足すと終了コード 0
再現から修正までは errfix の検証ハーネスが機械的に確認しています。Gradle の版だけを変えた対照(同一ソースで gradle:8.14.3-jdk21 が終了コード 0、gradle:9.0.0-jdk21 が 1)、junit-jupiter の版だけを変えた表(8.14.3 上で 5.10.2 と 5.11.4 が通り、5.12.0 / 5.12.1 / 5.12.2 / 5.13.4 が失敗)、gradle:8.10.2-jdk21 でも 5.12.2 が同じ症状で失敗すること、Gradle 8.14.3 の非推奨警告の文言、Gradle 9.1.0 のメッセージ、失敗時のテストレポート XML の中身は、いずれも同じ手順で実測しています。
launcher が宣言前の testRuntimeClasspath に 1 行も現れないこと、宣言後は junit-bom によって engine と同じ版に揃うことは、どちらも gradle dependencies --configuration testRuntimeClasspath の解決結果で確認しています。
Gradle 8 が注入する launcher の版は観測できていません(注入先がテスト実行プロセス側で、test タスクの classpath からは見えません)。版が連動しないことは、junit-jupiter だけを動かした成否の表で示しています。
Test Suites DSL・Kotlin DSL・Spring Boot 構成・Maven/surefire は実測していません。 Spring Boot と Maven についての切り分けは spring-projects/spring-boot#46037 の本文とコメント(Spring Boot と Gradle のメンテナによる説明)に拠ります。Android Gradle Plugin と Gradle の版の対応は Android の公式ドキュメントに拠ります。