如何在Gradle中使用JUnit 5?

65
我正在尝试在成功运行JUnit 4测试之后,使用Gradle来使用JUnit 5。
预期结果:JUnit 4测试在输出中显示了一个漂亮的“通过”并生成了一个位于build/reports/tests目录下的HTML报告。
实际结果:下面的JUnit 5测试除了显示“(…) build successful”之外,没有输出任何内容。我知道测试实际上没有运行,因为没有测试日志输出通过/跳过/失败,并且在测试中放置一个“fail”也会使构建成功。
运行“gradle test --info”会显示“Skipping task ':testClasses' as it has no actions.”等很多我认为大部分是无关紧要的输出。令人惊讶的是,它还显示“Executing task ':test'”和“Generating HTML test report... Finished generating test html results”等类似的内容,以及在build/test-results/test目录下生成的xml文件,但实际上并没有生成xml文件,HTML报告显示没有运行任何测试和错误,并且测试确实没有运行。
我还觉得非常有趣的是,“gradle test --debug”会显示:
[TestEventLogger] Gradle Test Run :test STARTED
[org.gradle.api.internal.tasks.testing.junit.JUnitDetector] test-class-
scan : failed to scan parent class java/lang/Object, could not find the class file
[TestEventLogger]
[TestEventLogger] Gradle Test Run :test PASSED

我的唯一测试包含
fail("test fails");

我觉得这很奇怪!
我的构建文件是
apply plugin: 'java'

test {
    dependsOn 'cleanTest' // run tests every time

}

sourceSets {
    main {
        java {
            srcDirs 'src'
        }
    }
    test {
        java {
            srcDirs 'test'
        }
    }
}

repositories {
    mavenCentral()
}

dependencies {
    // when using this, it worked with a junit 4 test
//    testCompile 'junit:junit:4.10'
    // this should be needed for junit 5 (using M4 is required since IJ 2017.1.2
    testCompile("org.junit.jupiter:junit-jupiter-api:5.0.0-M4")
}

test {
    testLogging {
        events "passed", "skipped", "failed"
    }
}

我的测试是
package mypackage;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class HelloWorldTest {
    @Test
    public void testHelloWorld(){
        assertEquals(2, 1+1, "message");
    }
}

我的文件夹结构是使用包mypackage
java-template-project
--- src
    --- mypackage
        --- HelloWorld.java
--- test
    --- mypackage
        --- HelloWorldTest.java

在我正在使用的IntelliJ 2017.1.3中,模块结构如下所示。
java-template-project
--- java-template-project_main
    --- src/mypackage
        --- HelloWorld(.java)
--- java-template-project_test
    --- test/mypackage
        --- HelloWorldTest(.java)

因为现在的Gradle要求源代码和测试代码放在各自的包中。
我尝试过的方法是:
显然,这不是关于这个主题的第一个问题,我找到的所有相关问题都是

2
遇到了同样的问题。发现 @Test 应该从 'org.junit.jupiter.api'(Junit5 @Test)导入,而不是 org.junit(Junit4 @Test)。 - yname
7个回答

59

新功能:Gradle 4.6支持JUnit 5

正如在此GitHub问题中指出的那样,从Gradle 4.6开始,支持JUnit 5!请查阅4.6版本的官方发布说明(这是最新版本,但请检查GitHub发布页面以确保您使用的是最新版本)位于docs.gradle.org上。旧设置仍将起作用,但使用此设置可以使构建文件更加简洁。

[2019年5月编辑]正如@deFreitas在他的答案中所指出的那样,JUnit文档已得到改进,现在提供了一个完整的示例,请参见https://github.com/junit-team/junit5-samples/tree/r5.4.0/junit5-jupiter-starter-gradle,特别是其中的build.gradle文件。幸运的是,它实际上与此答案中的文件相同。

更新Gradle

首先,请确保您正在使用最新版本的Gradle,请查阅GitHub发布以获取最新版本。例如,如果是4.6,则在项目位置的终端中运行gradlew wrapper --gradle-version=4.6或确保更新gradle/wrapper/gradle-wrapper.properties文件中的以下行:distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip

如何使用内置的JUnit 5

然后,将java文件、目录结构等从问题中提取出来,build.gradle文件将是(使用新的plugins块)

plugins {
    id 'java'
}

repositories {
    mavenCentral()
    mavenLocal()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.0.3'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.0.3'
}

// These lines can be removed when you use the default directories src/main/kotlin and src/test/kotlin
sourceSets {
    main.java.srcDirs += 'src'
    main.resources.srcDirs += 'src'
    test.java.srcDirs += 'test'
    test.resources.srcDirs += 'test'
}

// Java target version
sourceCompatibility = 1.8

test {
    // Enable JUnit 5 (Gradle 4.6+).
    useJUnitPlatform()

    // Always run tests, even when nothing changed.
    dependsOn 'cleanTest'

    // Show test results.
    testLogging {
        events "passed", "skipped", "failed"
    }
}

PS:要想获得绝对最小版本,请参见Ray's answer

Android(请参见此帖子:JUnit 5 for Android testing

在Android上,我通过将以下内容添加到我的应用程序模块构建文件中来运行问题的JUnit 5测试。如您所见,依赖关系相同,但我不需要使用useJUnitPlatform()且测试配置块略有不同。

apply plugin: 'com.android.application'
// In fact I am not sure you need this, but I had it included to run Spek tests anyway
apply plugin: 'de.mannodermaus.android-junit5' 

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
}


android {
    // I'm omitting your other configurations like compileSdkVersion, buildTypes etc.

    testOptions {
        unitTests.all {

            // Always run tests, even when nothing changed.
            dependsOn 'clean'

            // Show test results.
            testLogging {
                events "passed", "skipped", "failed"
            }
        }
    }
}

然而,只有当我执行Gradle的test任务时才能正常工作,而非运行check任务。通常情况下,我通过创建一个失败的测试来测试它,然后尝试Gradle任务是否通过或失败。


1
在撰写本文时,Gradle 的内置支持仍缺少一些可能对您很重要的功能。请参阅以下 Github 问题:https://github.com/gradle/gradle/issues/4605 - johanneslink
1
@toffor 是的,我能够成功运行题目中的测试(注意导入语句,它们与 JUnit 4 不同),只需将这两个依赖项添加到我的现有 Android 应用程序中,然后使用 Gradle 的 check 任务即可。我会在答案中添加这一点。 - PHPirate
对于较新版本的Gradle(我测试了Gradle 7.0.2),请在Groovy DSL中将test {替换为tasks.withType(Test) {,或在Kotlin DSL(build.gradle.kts)中将其替换为tasks.withType<Test> {。请参见此帖子 - Mahozad
@Mahozad 你是指在Android上吗?因为对于桌面应用程序,我觉得它现在已经很好了,Kotlin DSL的语法是tasks.test {,而Groovy的语法是test {。无论如何,感谢您添加链接到您的Android答案。 - PHPirate
我认为你是正确的。它在安卓上。 不客气。 - Mahozad
显示剩余2条评论

25

你需要为两个 JUnit 版本引入引擎,并且需要应用 JUnit 平台 Gradle 插件。我在你的 Gradle 文件中没有看到这一点。这是一个可以同时执行 JUnit 4 和 5 的工作 Gradle 构建:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath ("org.junit.platform:junit-platform-gradle-plugin:1.0.0-M4")
    }
}

apply plugin: 'org.junit.platform.gradle.plugin'
...

dependencies {
...
    testCompile("junit:junit:4.12")
    testRuntime("org.junit.vintage:junit-vintage-engine:4.12.0-M4")

    testCompile("org.junit.jupiter:junit-jupiter-api:5.0.0-M4")
    testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0-M4")

    // Enable use of the JUnitPlatform Runner within the IDE
    testCompile("org.junit.platform:junit-platform-runner:1.0.0-M4")
}

junitPlatform {
    details 'tree'
}

请查看JUnit文档以获取更多信息。


15

仅仅是为了增加知识库,我刚刚使用Gradle 4.7成功实现如下:

apply plugin: 'java'
repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.1.1'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.1.1'
}

test {
    useJUnitPlatform()
}

2
感谢您添加了一个更简洁的示例!我认为使用plugins块而不是apply plugin未来,但目前并不是关键。 - PHPirate

7

由于 github 问题,JUnit 5内置支持将在Gradle 4.6中得到解决。

因此,自gradle 4.6以来,您的期望结果必须与实际结果相同。

预期结果:JUnit 4测试会在输出中显示“已通过”,并在build/reports/tests中提供HTML报告。

更新:

gradle 4.6-rc-1于2018年2月16日发布,此版本支持内置junit 5。

要启用junit 5支持,您需要更新gradle包装器:

gradle wrapper --gradle-version=4.6-rc-1

只需在build.gradle文件中添加一行代码:

test {
    useJUnitPlatform()
}

4

请查看junit官方文档,了解如何在gradle中使用junit 5。

build.gradle

plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation('org.junit.jupiter:junit-jupiter:5.4.0')
}

test {
    useJUnitPlatform()
    testLogging {
        events "passed", "skipped", "failed"
    }
}

1
谢谢提供链接!很高兴看到自从这个问题被提出以来,他们已经改进了文档。我会在我的回答中添加一个链接。 - PHPirate

1
对于Gradle Kotlin DSL(build.gradle.kts),以下是你需要的一切,以便能够同时运行JUnit 5和/或JUnit 4测试:
tasks.withType<Test> {
    useJUnitPlatform() // Enables JUnit Platform (JUnit 5 + JUnit 4)
}

dependencies {
    // Aggregator dependency that also brings JUnit 5 parameterized tests etc.
    testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")

    // Include the Vintage engine to be able to run JUnit 4 tests as well
    testImplementation("org.junit.vintage:junit-vintage-engine:5.10.0")
}

src/test/java/com/example/MyTestClass.java:

package com.example

import org.junit.jupiter.api.Test
//...

public class MyTestClass {
    @Test // <- JUnit 5 test
    public void jUnit5test() {
        // ...
    }

    @org.junit.Test // <- JUnit 4 test
    public void jUnit4Test() {
        // ...
    }
}

执行您的测试,通过运行Gradle任务testcheck,就像这样:
./gradlew test

或者,只执行一些测试:
./gradlew test --tests "com.example.MyTestClass"

0
也许这对于那些在尝试将JUnit5与Gradle版本4.10集成时遇到问题的人有所帮助。
Could not find method test() for arguments [build_dzas89s5z18l3bfyn6b3q0dxv$_run_closure2$_closure9@8e60c6] on project ':app' of type org.gradle.api.Project.

实际上,使用4.10版本的时候,您不需要在build.gradle中添加此测试配置块来启用JUnit5。

test {
    useJUnitPlatform()
}

只需添加jupitor-api和jupitor-engine的必要依赖项,它应该可以正常工作。

我尝试查看4.10版本的发布说明,但没有找到有关此更改的任何信息。如果有人知道背后的原因,请也让我知道。


感谢您回答,虽然我无法复现这个错误,但我仍需要useJUnitPlatform(),否则我的测试将无法运行(即使在Gradle 5.0上)。我很好奇这里发生了什么。不管怎样,希望这对其他人有所帮助。(编辑:也许您正在为Android项目使用Gradle。那可能是不同之处。) - PHPirate
我遇到了类似的错误:Could not find method test() for arguments [build_ejzeh5kmeasn8zq3a46rzrkon$_run_closure4@49e05c95] on root project 'xxxxx' of type org.gradle.api.Project.,我认为这是因为我把 test { useJUnitPlatform() } 放在了 subprojects { ... } 的外面,如果我把它移到 subprojects { ... } 的里面,这个错误就消失了。 - Li Ying

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接