38

Using spring-boot-starter-test as of 2.0.6 brings in a JUnit 4 dependency. How can I use spring-boot-starter-test (via Gradle), but use JUnit 5 instead, without the JUnit 4 dependency being pulled in?

Here's a part of the dependency output from Gradle if it helps:

+--- org.springframework.boot:spring-boot-starter-test -> 2.0.5.RELEASE
|    +--- org.springframework.boot:spring-boot-starter:2.0.5.RELEASE (*)
|    +--- org.springframework.boot:spring-boot-test:2.0.5.RELEASE
|    |    \--- org.springframework.boot:spring-boot:2.0.5.RELEASE (*)
|    +--- org.springframework.boot:spring-boot-test-autoconfigure:2.0.5.RELEASE
|    |    +--- org.springframework.boot:spring-boot-test:2.0.5.RELEASE (*)
|    |    \--- org.springframework.boot:spring-boot-autoconfigure:2.0.5.RELEASE (*)
|    +--- com.jayway.jsonpath:json-path:2.4.0
|    |    +--- net.minidev:json-smart:2.3
|    |    |    \--- net.minidev:accessors-smart:1.2
|    |    |         \--- org.ow2.asm:asm:5.0.4
|    |    \--- org.slf4j:slf4j-api:1.7.25
|    +--- junit:junit:4.12
|    |    \--- org.hamcrest:hamcrest-core:1.3


Here is my build.gradle file:

buildscript {
    ext {
        springBootVersion = '2.0.6.RELEASE'
    rootGradleDir = "${rootProject.rootDir}/gradle"
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

apply from: "${rootGradleDir}/staticCodeAnalysis.gradle"

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

test {
  useJUnitPlatform()
}

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-data-jpa')
    implementation('org.springframework.boot:spring-boot-starter-jdbc')
    implementation('org.springframework.boot:spring-boot-starter-security')
    implementation('org.springframework.boot:spring-boot-starter-thymeleaf')
    implementation('org.springframework.boot:spring-boot-starter-validation')
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.liquibase:liquibase-core')
    runtimeOnly('org.springframework.boot:spring-boot-devtools')
  runtimeOnly('org.postgresql:postgresql')
  testImplementation('org.springframework.boot:spring-boot-starter-test')
  testImplementation('org.springframework.security:spring-security-test')

  implementation('org.glassfish.jaxb:jaxb-runtime:2.3.1')
  implementation('org.glassfish.jaxb:jaxb-runtime2.3.1')
  implementation('org.springframework.boot:spring-boot-starter-data-redis')

  testCompile('org.junit.jupiter:junit-jupiter-api:5.3.1')
  testCompile('org.junit.jupiter:junit-jupiter-params:5.3.1')
  testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.1')
}

UPDATE

Adding the JUnit 5 dependency and doing the exclude mentioned in the comments did the trick. The test dependency now looks like this:

testImplementation('org.springframework.boot:spring-boot-starter-test') {
    exclude group: 'junit', module: 'junit' //by both name and group
}
informatik01
  • 16,038
  • 10
  • 74
  • 104
user605331
  • 3,718
  • 4
  • 33
  • 60

6 Answers6

22

A few additional notes to the ones mentioned by other contributors:

Using Spring Boot > 2.4.0

If you are using Spring Boot > 2.4.0, then there is nothing you have to do to use JUnit 5 Jupiter, because the spring-boot-starter-test library no longer includes the vintage-engine dependency (which transitively included JUnit 4), just include the starter dependency to the project and you're good to go. Use the useJUnitPlatform() in the Gradle configuration.

Using 2.4.0 > Spring Boot > 2.2.0

If you use earlier versions, I'd suggest using a version higher than 2.2.0.RELEASE, which is where the Spring team added support for JUnit 5 Jupiter into the spring-boot-starter-test by default.

In these versions, the library included the Vintage Engine dependency too, which could be used to run JUnit 4 tests using the JUnit 5 Jupiter platform. If you don't need to execute JUnit 4 tests, then the spring team suggests excluding org.junit.vintage:junit-vintage-engine (not just junit as indicated in the description):

testCompile('org.springframework.boot:spring-boot-starter-test') {
    exclude group: 'org.junit.vintage'
}

Here you would also need to configure the useJUnitPlatform() directive, of course.

Gerardo Roza
  • 2,746
  • 2
  • 24
  • 33
18

As of Gradle 4.6 (I believe), there is native JUnit 5 support. You can just include JUnit5 as follows:

dependencies {
  testCompile "org.junit.jupiter:junit-jupiter-api:5.2.0"
  testCompile "org.junit.jupiter:junit-jupiter-params:5.2.0"
  testRuntime "org.junit.jupiter:junit-jupiter-engine:5.2.0"
}

You will also need:

test {
  useJUnitPlatform()
}

JUnit 4 and 5 use different package names, so they can co-exist in the same project. Many of the annotations are the same (@Test, etc) so make sure you include them from the org.junit.jupiter.api package.

Mike
  • 4,722
  • 1
  • 27
  • 40
  • Thanks. I've done that as well, but the JUnit 4 dependency still comes in. It sounds like the exclude route mentioned by @lealceldeiro is the way to go here. – user605331 Oct 17 '18 at 20:02
  • Correct, you would need to exclude it if you dont want it to be included at all. – Mike Oct 17 '18 at 20:30
11

Here's using implementation instead of compile.

test {
  useJUnitPlatform()
}

dependencies {
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'junit', module: 'junit'
    }
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.2'
    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.4.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.2'
}

Update 2020-10-29

In our Spring Boot 2.3 project, we didn't need this snippet anymore. It was already using JUnit 5 by default.

Bienvenido David
  • 4,118
  • 1
  • 25
  • 16
5

It appears newer versions of spring-boot-starter-test (noticed in 2.2.6/2.2.7) are importing org.junit.vintage:junit-vintage-engine, which has a transitive dependency on junit:junit. Excluding just junit gave me some spurious errors:

May 13, 2020 9:20:05 AM org.junit.platform.launcher.core.DefaultLauncher handleThrowable
WARNING: TestEngine with ID 'junit-vintage' failed to discover tests
java.lang.NoClassDefFoundError: junit/runner/Version
...
Caused by: java.lang.ClassNotFoundException: junit.runner.Version
...

Setting:

testCompile('org.springframework.boot:spring-boot-starter-test') {
    exclude group: 'org.junit.vintage'
}

did the trick for me and all tests continue to run fine

Patrick Herrera
  • 3,120
  • 26
  • 21
3

Ten months later, and using Gradle 5.4.1, Spring Boot 2.1.7.RELEASE, and JUnit 5.5.1, I found that I was using a different dependency than the other answers. Also, I found that it is better to include the aggregate artifact rather than the individual artifacts:

testImplementation('org.junit.jupiter:junit-jupiter:5.5.1')

You can see more about my observation from this other SO Q&A of mine: Interaction between Spring Boot and JUnit 5 — must use the overall artifacts not the individuals?

leeyuiwah
  • 6,562
  • 8
  • 41
  • 71
0

Just add

testImplementation('org.springframework.boot:spring-boot-starter-test') {
    exclude group: 'junit', module: 'junit'
}
testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2'

And:

test {
    useJUnitPlatform()
}
José Braz
  • 575
  • 5
  • 9