This Gradle project:

dependencies {
    testImplementation(platform("org.springframework.boot:spring-boot-dependencies:3.4.6"))
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.test {
    useJUnitPlatform()
}

works in the current stable Gradle version (8.14.2), but fails in Gradle 9.0.0-rc-1 with:

Execution failed for task ':test'.
> 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.

According to Gradle developers, the behaviour of Gradle 9 is as expected, due to a removal of deprecated functionality (of Gradle guessing which version of junit-platform-launcher to add to classpath): https://github.com/gradle/gradle/issues/33950#issuecomment-2989035089

There are people writing that the deprecated functionality caused problems with Spring Boot 3.5.0: https://discuss.gradle.org/t/why-do-gradle-docs-specify-junit-platform-launcher-for-junit-5-tests/46286/8. This seems to confirm the removal in Gradle 9 is a good decision.

So I think the move should be on Spring Boot side, by adding a runtime dependency on junit-platform-launcher to spring-boot-starter-test.

Comment From: philwebb

I don't think we can add the dependency by default because it's likely to break other build systems. We already have some code that adds the dependency if you're using our Gradle plugin.

@pkubowicz can you check you have something like this in your build:

plugins {
  id 'java'
  id 'org.springframework.boot' version '3.5.2'
  id 'io.spring.dependency-management' version '1.1.7'
}

If you do, could you share a complete project that reproduces the problem.

Comment From: pkubowicz

Should I use org.springframework.boot plugin in modules that don't produce runnable JARs? I thought there is no need to, as the plugin is to produce a JAR with Spring Boot launcher inside.

I can have a project that has 10 modules with tests each, gathered into a runnable Spring Boot application in an 11th module that applies org.springframework.boot plugin.

There are even third-party plugins that claim using java-library together with org.springframework.boot is incorrect: https://github.com/autonomousapps/dependency-analysis-gradle-plugin/issues/1270

Comment From: wilkinsona

Should I use org.springframework.boot plugin in modules that don't produce runnable JARs?

No, you shouldn't. In those cases you should configure the necessary dependency yourself or use Gradle's test suites where explicitly declaring the dependency is not necessary.

We can't add the dependency to the starter as it isn't relevant for Maven users. I'm going to close this one as it sounds like things are working as designed when the org.springframework.boot plugin is applied (and my own testing also shows this to be the case).

Comment From: big-guy

We can't add the dependency to the starter as it isn't relevant for Maven users. I'm going to close this one as it sounds like things are working as designed when the org.springframework.boot plugin is applied (and my own testing also shows this to be the case).

It looks like Maven has special handling to add the missing dependency too (PROVIDER_DEP_AID is junit-platform-launcher): https://github.com/apache/maven-surefire/blob/04afe8ff53b199b12581e7cf6a9370a406696c10/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java#L3185-L3188

IIUC, this checks if the launcher dependency is available and if it isn't, it automatically adds one based on the version of junit-platform-runner.

I think this means that for both Maven (for surefire) and Gradle (for spring boot) there are different workarounds to add this runtime dependency because it is missing from the metadata.