JVM 测试套件插件
JVM 测试套件插件(插件 ID:jvm-test-suite
)提供了 DSL 和 API,用于在基于 JVM 的项目中将多组自动化测试建模为测试套件。测试套件旨在按其目的分组,并且可以具有独立的依赖项并使用不同的测试框架。
例如,此插件可用于定义一组集成测试,这些测试可能比单元测试运行时间长得多,并且具有不同的环境要求。
JVM 测试套件插件是一个孵化中的 API,可能会在未来的版本中更改。 |
用法
此插件由 java
插件自动应用,但如果需要,也可以显式应用。如果未应用 JVM 语言插件,则无法使用此插件,因为它依赖于 java
插件的几个约定。
plugins {
java
`jvm-test-suite`
}
plugins {
id 'java'
id 'jvm-test-suite'
}
该插件将以下对象添加到项目中
-
一个
testing
扩展(类型:TestingExtension)到项目中,用于配置测试套件。
与Java 插件一起使用时
-
一个名为
test
的测试套件(类型:JvmTestSuite)。 -
一个
test
SourceSet。 -
几个派生自
test
SourceSet 名称的配置:testImplementation
、testCompileOnly
、testRuntimeOnly
-
一个由名为
test
的任务支持的单个测试套件目标。
test
任务、SourceSet 和派生配置的名称和功能与早期 Gradle 版本中使用的相同。
配置
请参阅 API 文档中的 TestingExtension 类。
声明一个额外的测试套件
testing {
suites { (1)
val test by getting(JvmTestSuite::class) { (2)
useJUnitJupiter() (3)
}
register<JvmTestSuite>("integrationTest") { (4)
dependencies {
implementation(project()) (5)
}
targets { (6)
all {
testTask.configure {
shouldRunAfter(test)
}
}
}
}
}
}
tasks.named("check") { (7)
dependsOn(testing.suites.named("integrationTest"))
}
testing {
suites { (1)
test { (2)
useJUnitJupiter() (3)
}
integrationTest(JvmTestSuite) { (4)
dependencies {
implementation project() (5)
}
targets { (6)
all {
testTask.configure {
shouldRunAfter(test)
}
}
}
}
}
}
tasks.named('check') { (7)
dependsOn(testing.suites.integrationTest)
}
1 | 为此项目配置所有测试套件。 |
2 | 配置内置的 test 套件。此套件是为向后兼容性自动创建的。您**必须**指定要使用的测试框架才能运行这些测试(例如 JUnit 4、JUnit Jupiter)。此套件是唯一可以自动访问生产源 implementation 依赖项的套件,所有其他套件都必须显式声明这些依赖项。 |
3 | 声明此测试套件使用 JUnit Jupiter。框架的依赖项会自动包含。即使需要 JUnit4,也始终有必要显式配置内置的 test 套件。 |
4 | 定义一个名为 integrationTest 的新套件。请注意,除了内置的 test 套件之外的所有套件都将按约定工作,就像调用了 useJUnitJupiter() 一样。您**不需要**在这些额外套件上显式配置测试框架,除非您希望更改为另一个框架。 |
5 | 向 integrationTest 套件目标添加对项目生产代码的依赖项。按照惯例,只有内置的 test 套件会自动依赖于项目的生产代码。 |
6 | 配置此套件的所有目标。按照惯例,测试套件目标与其他 Test 任务没有关系。此示例显示较慢的集成测试套件目标应在 test 套件的所有目标完成后运行。 |
7 | 将 check 任务配置为依赖于所有 integrationTest 目标。按照惯例,测试套件目标不与 check 任务关联。 |
在上述配置的构建上调用 check
任务应显示类似以下内容的输出
> Task :compileJava > Task :processResources NO-SOURCE > Task :classes > Task :jar > Task :compileTestJava > Task :processTestResources NO-SOURCE > Task :testClasses > Task :test > Task :compileIntegrationTestJava > Task :processIntegrationTestResources NO-SOURCE > Task :integrationTestClasses > Task :integrationTest > Task :check BUILD SUCCESSFUL in 0s 6 actionable tasks: 6 executed
请注意,integrationTest
测试套件直到 test
测试套件完成之后才会运行。
配置内置的 test
套件
testing {
suites {
val test by getting(JvmTestSuite::class) {
useTestNG() (1)
targets {
all {
testTask.configure { (2)
// set a system property for the test JVM(s)
systemProperty("some.prop", "value")
options { (3)
val options = this as TestNGOptions
options.preserveOrder = true
}
}
}
}
}
}
}
testing { (1)
suites {
test {
useTestNG() (1)
targets {
all {
testTask.configure { (2)
// set a system property for the test JVM(s)
systemProperty 'some.prop', 'value'
options { (3)
preserveOrder = true
}
}
}
}
}
}
}
1 | 声明 test 测试套件使用 TestNG 测试框架。 |
2 | 延迟配置套件所有目标的测试任务;请注意 testTask 的返回类型是 TaskProvider<Test> 。 |
3 | 配置更详细的测试框架选项。options 将是 org.gradle.api.tasks.testing.TestFrameworkOptions 的子类,在这种情况下是 org.gradle.api.tasks.testing.testng.TestNGOptions 。 |
配置测试套件的依赖项
testing {
suites {
val test by getting(JvmTestSuite::class) { (1)
dependencies {
// Note that this is equivalent to adding dependencies to testImplementation in the top-level dependencies block
implementation("org.assertj:assertj-core:3.21.0") (2)
annotationProcessor("com.google.auto.value:auto-value:1.9") (3)
}
}
}
}
testing {
suites {
test { (1)
dependencies {
// Note that this is equivalent to adding dependencies to testImplementation in the top-level dependencies block
implementation 'org.assertj:assertj-core:3.21.0' (2)
annotationProcessor 'com.google.auto.value:auto-value:1.9' (3)
}
}
}
}
1 | 配置内置的 test 测试套件。 |
2 | 将 assertj 库添加到测试的编译和运行时类路径。测试套件内的 dependencies 块已限定在该测试套件的范围内。测试套件无需知道配置的全局名称,而是具有一致的名称,您可以在此块中使用该名称声明 implementation 、compileOnly 、runtimeOnly 和 annotationProcessor 依赖项。 |
3 | 将 Auto Value 注解处理器添加到套件的注解处理器类路径中,以便在编译测试时运行它。 |
配置测试套件的依赖项以引用项目输出
dependencies {
api("com.google.guava:guava:30.1.1-jre") (1)
implementation("com.fasterxml.jackson.core:jackson-databind:2.13.3") (2)
}
testing {
suites {
val integrationTest by registering(JvmTestSuite::class) {
dependencies {
implementation(project()) (3)
}
}
}
}
dependencies {
api 'com.google.guava:guava:30.1.1-jre' (1)
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3' (2)
}
testing {
suites {
integrationTest(JvmTestSuite) {
dependencies {
implementation project() (3)
}
}
}
}
1 | 添加一个生产依赖项,作为库公共 API 的一部分使用。 |
2 | 添加一个仅在内部使用且不作为此项目公共类的一部分公开的生产依赖项。 |
3 | 配置一个新的测试套件,该套件向套件的编译和运行时类路径添加 project() 依赖项。此依赖项提供对项目输出以及其 api 和 compileOnlyApi 配置上声明的任何依赖项的访问。 |
配置测试套件的源目录
testing {
suites {
val integrationTest by registering(JvmTestSuite::class) { (1)
sources { (2)
java { (3)
setSrcDirs(listOf("src/it/java")) (4)
}
}
}
}
}
testing {
suites {
integrationTest(JvmTestSuite) { (1)
sources { (2)
java { (3)
srcDirs = ['src/it/java'] (4)
}
}
}
}
}
1 | 声明并配置一个名为 integrationTest 的套件。SourceSet 和合成的 Test 任务将基于此名称。 |
2 | 配置测试套件的 sources 。 |
3 | 配置测试套件的 java SourceDirectorySet。 |
4 | 覆盖 srcDirs 属性,将常规的 src/integrationTest/java 位置替换为 src/it/java 。 |
配置测试套件的 Test
任务
testing {
suites {
val integrationTest by getting(JvmTestSuite::class) {
targets {
all { (1)
testTask.configure {
setMaxHeapSize("512m") (2)
}
}
}
}
}
}
testing {
suites {
integrationTest {
targets {
all { (1)
testTask.configure {
maxHeapSize = '512m' (2)
}
}
}
}
}
}
1 | 配置通过声明同名套件创建的 integrationTest 任务。 |
2 | 配置 Test 任务属性。 |
与测试套件目标关联的 Test
任务也可以直接按名称配置。无需通过测试套件 DSL 进行配置。
在多个测试套件之间共享配置
有几种方法可以在多个测试套件之间共享配置,以避免依赖项或其他配置的重复。每种方法都是灵活性和使用声明式与命令式配置样式之间的权衡。
-
在
suites
容器上使用configureEach
方法以相同的方式配置每个测试套件。 -
使用
withType
和matching
以及configureEach
过滤测试套件并配置它们的子集。 -
将配置块提取到局部变量并仅将其应用于所需的测试套件。
方法 1:使用 configureEach
这是在每个测试套件之间共享配置的最直接方法。配置应用于每个测试套件。
testing {
suites {
withType<JvmTestSuite> { (1)
useJUnitJupiter()
dependencies { (2)
implementation("org.mockito:mockito-junit-jupiter:4.6.1")
}
}
(3)
val integrationTest by registering(JvmTestSuite::class)
val functionalTest by registering(JvmTestSuite::class) {
dependencies { (4)
implementation("org.apache.commons:commons-lang3:3.11")
}
}
}
}
testing {
suites {
configureEach { (1)
useJUnitJupiter()
dependencies { (2)
implementation('org.mockito:mockito-junit-jupiter:4.6.1')
}
}
(3)
integrationTest(JvmTestSuite)
functionalTest(JvmTestSuite) {
dependencies { (4)
implementation('org.apache.commons:commons-lang3:3.11')
}
}
}
}
1 | 配置每个 JVM 测试套件 |
2 | 提供所有测试套件共享的依赖项 |
3 | 定义将在创建时配置的其他测试套件 |
4 | 添加特定于 functionalTest 测试套件的依赖项 |
方法 2:使用 withType
、matching
和 configureEach
此方法添加过滤命令,仅将配置应用于测试套件的子集,由名称标识。
testing {
suites {
withType(JvmTestSuite::class).matching { it.name in listOf("test", "integrationTest") }.configureEach { (1)
useJUnitJupiter()
dependencies {
implementation("org.mockito:mockito-junit-jupiter:4.6.1")
}
}
val integrationTest by registering(JvmTestSuite::class)
val functionalTest by registering(JvmTestSuite::class) {
useJUnit() (2)
dependencies { (3)
implementation("org.apache.commons:commons-lang3:3.11")
}
}
}
}
testing {
suites {
withType(JvmTestSuite).matching { it.name in ['test', 'integrationTest'] }.configureEach { (1)
useJUnitJupiter()
dependencies {
implementation('org.mockito:mockito-junit-jupiter:4.6.1')
}
}
integrationTest(JvmTestSuite)
functionalTest(JvmTestSuite) {
useJUnit() (2)
dependencies { (3)
implementation('org.apache.commons:commons-lang3:3.11')
}
}
}
}
1 | 配置符合给定条件的每个 JVM 测试套件 |
2 | 为 functionalTest 测试套件使用不同的测试框架 |
3 | 添加特定于 functionalTest 测试套件的依赖项 |
方法 3:提取自定义配置块
此方法最灵活,但也是最命令式的。
testing {
suites {
val applyMockito = { suite: JvmTestSuite -> (1)
suite.useJUnitJupiter()
suite.dependencies {
implementation("org.mockito:mockito-junit-jupiter:4.6.1")
}
}
/* This is the equivalent of:
val test by getting(JvmTestSuite::class) {
applyMockito(this)
}
*/
val test by getting(JvmTestSuite::class, applyMockito) (2)
/* This is the equivalent of:
val integrationTest by registering(JvmTestSuite::class)
applyMockito(integrationTest.get())
*/
val integrationTest by registering(JvmTestSuite::class, applyMockito) (3)
val functionalTest by registering(JvmTestSuite::class) {
useJUnit()
dependencies {
implementation("org.apache.commons:commons-lang3:3.11")
}
}
}
}
testing {
suites {
def applyMockito = { suite -> (1)
suite.useJUnitJupiter()
suite.dependencies {
implementation('org.mockito:mockito-junit-jupiter:4.6.1')
}
}
/* This is the equivalent of:
test {
applyMockito(this)
}
*/
test(applyMockito) (2)
/* This is the equivalent of:
integrationTest(JvmTestSuite)
applyMockito(integrationTest)
*/
integrationTest(JvmTestSuite, applyMockito) (3)
functionalTest(JvmTestSuite) {
useJUnit()
dependencies {
implementation('org.apache.commons:commons-lang3:3.11')
}
}
}
}
1 | 定义一个接受单个 JvmTestSuite 参数并配置它的闭包 |
2 | 使用默认(test )测试套件将闭包应用于测试套件 |
3 | 在测试套件声明之外将配置闭包应用于测试套件的替代方法,使用 integrationTest 测试套件 |
JvmTestSuite 和 Test 任务类型上相似方法之间的区别
JvmTestSuite 实例具有 useJUnit() 和 useJUnitJupiter() 方法,这些方法与 Test 任务类型上的方法(useJUnit() 和 useJUnitPlatform())名称相似。但是,存在重要区别。
与 Test 任务上的方法不同,JvmTestSuite 方法执行两个额外的配置
-
JvmTestSuite#useJUnit()、#useJUnitJupiter() 和其他特定于框架的方法会自动将相关的测试框架库应用于套件目标的编译和运行时类路径。请注意,JvmTestSuite#useJUnit(String) 和 #useJUnitJupiter(String) 等重载方法允许您提供特定版本的框架依赖项。
-
JvmTestSuite#useJUnit() 和 #useJUnitJupiter() 会自动配置套件目标的 Test 任务,使其使用指定的框架执行。
此行为的示例显示在上面的第一个配置示例中,配置内置的 test
套件。
与 Test 任务不同,JvmTestSuite 上述方法不接受用于配置框架的闭包或操作。这些框架配置选项可以在各个目标上设置。 |
输出变体
每个测试套件都会创建一个包含其测试执行结果的输出变体。这些变体旨在供 测试报告聚合插件 使用。
属性将类似于以下内容。用户可配置的属性在示例下方突出显示。
--------------------------------------------------
Variant testResultsElementsForTest (i)
--------------------------------------------------
Description = Directory containing binary results of running tests for the test Test Suite's test target.
Capabilities
- org.gradle.sample:list:1.0.2 (default capability)
Attributes
- org.gradle.category = verification
- org.gradle.testsuite.name = test (1)
- org.gradle.verificationtype = test-results
Artifacts
- build/test-results/test/binary (artifactType = directory)
1 | TestSuiteName 属性;值派生自 TestSuite#getName()。 |