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 類別。
術語和建模
JVM 測試套件外掛會導入一些由新 API 支援的建模概念。以下是其定義。
測試套件
測試套件是 JVM-based 測試的集合。
測試套件目標
對於這個外掛的初始版本,每個測試組件都有單一目標。這會導致測試組件、測試組件目標和匹配的 測試 任務之間產生 1:1:1 的關係。測試
任務的名稱來自組件名稱。外掛的未來迭代將允許根據其他屬性(例如特定 JDK/JRE 執行時間)定義多個目標。
每個測試組件都有一些組態,這些組態在組件中包含的所有測試中都是通用的
-
測試架構
-
來源
-
相依性
在未來,可以在測試組件中指定其他屬性,這些屬性可能會影響選取來編譯和執行測試的工具鏈。
與目標關聯的 測試
任務從組件繼承其名稱。測試
任務的其他屬性是可以組態的。
測試組件類型
必須為每個測試組件指定一個類型。類型可用於在建置中將相關測試組件分組到多個 Gradle 專案。
可以使用組件的 測試類型屬性 來組態測試組件的類型。在同一個 Gradle 專案中的所有測試組件中,類型必須是唯一的。根據慣例,類型會設定為測試組件的名稱,轉換為短橫線分隔形式 - 內建測試組件例外,它會使用值 'unit-test'。
常見值在 TestSuiteType 中以常數形式提供。
組態範例
以下有幾個範例來說明測試組件的可組態性。
宣告一個額外的測試組件
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 | 組態內建的 測試 組件。這個組件會自動建立,以提供向後相容性。您必須指定一個測試架構才能執行這些測試(例如 JUnit 4、JUnit Jupiter)。這個組件是唯一會自動存取生產來源的 實作 相依性的組件,所有其他組件都必須明確宣告這些相依性。 |
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 。 |
設定測試套件的類型
testing {
suites {
val secondaryTest by registering(JvmTestSuite::class) {
testType = TestSuiteType.INTEGRATION_TEST (1)
}
}
}
testing {
suites {
secondaryTest(JvmTestSuite) {
testType = TestSuiteType.INTEGRATION_TEST (1)
}
}
}
1 | 此套件預設會使用類型值 'secondary-test'。此設定會明確將類型設定為 'integration-test'。 |
設定測試套件的 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 測試套件,在宣告之外將組態閉包套用至測試套件的替代方式 |
測試套件 dependencies
和頂層 dependencies
區塊之間的差異
Gradle 7.6 變更測試套件 dependencies
區塊的 API,使其更強類型化。
每個相依性範圍會傳回一個 DependencyAdder
,提供強類型化的方法來新增和組態相依性。
還有一個 DependencyFactory
,其中包含用於根據不同表示法建立新相依性的工廠方法。相依性可以使用這些工廠方法延遲建立,如下所示。
此 API 與頂層 dependencies
區塊的差異如下
-
必須使用
String
、Dependency
執行個體、FileCollection
、Dependency
的Provider
或MinimalExternalModuleDependency
的ProviderConvertible
宣告相依性。 -
在 Gradle 建置指令碼之外,您必須明確呼叫
DependencyAdder
和add
的 getter。-
dependencies.add("implementation", x)
會變成getImplementation().add(x)
-
-
您無法使用 Kotlin 和 Java 的
Map
表示法宣告相依性。改用 Kotlin 和 Java 的多個引數方法。-
Kotlin:
compileOnly(mapOf("group" to "foo", "name" to "bar"))
會變成compileOnly(module(group = "foo", name = "bar"))
-
Java:
compileOnly(Map.of("group", "foo", "name", "bar"))
會變成getCompileOnly().add(module("foo", "bar", null))
-
-
您無法新增一個具有
Project
實例的相依性。您必須先將其轉換為ProjectDependency
。 -
您無法直接新增版本目錄套件。請改用每個組態上的
bundle
方法。-
Kotlin 和 Groovy:
implementation(libs.bundles.testing)
會變成implementation.bundle(libs.bundles.testing)
-
-
您無法直接使用非
Dependency
類型的提供者。請改用DependencyFactory
將它們對應到Dependency
。-
Kotlin 和 Groovy:
implementation(myStringProvider)
會變成implementation(myStringProvider.map { dependencyFactory.create(it) })
-
Java:
implementation(myStringProvider)
會變成getImplementation().add(myStringProvider.map(getDependencyFactory()::create)
-
測試套件內的 dependencies
區塊可能無法提供與頂層 dependencies
區塊相同的方法存取權。若要存取這些方法,您需要使用頂層 dependencies
區塊。
透過測試套件自己的 dependencies 區塊新增相依性是建議且推薦的方式。您也可以在建立測試套件後,在頂層 dependencies 區塊中存取測試套件的組態,但測試套件所使用的組態的確切名稱應視為可能會變更的實作細節。
|
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.testsuite.target.name = test (2)
- org.gradle.testsuite.type = unit-test (3)
- org.gradle.verificationtype = test-results
Artifacts
- build/test-results/test/binary (artifactType = directory)
1 | TestSuiteName 屬性;值取自 JvmTestSuite#getName()。 |
2 | TestSuiteTargetName 屬性;值取自 JvmTestSuiteTarget#getName()。 |
3 | TestSuiteType 屬性;值取自 JvmTestSuite#getTestType()。 |