Gradle Java 插件

2022-08-03 14:58 更新

Java 插件向一個(gè)項(xiàng)目添加了 Java 編譯、 測(cè)試和 bundling 的能力。它是很多其他 Gradle 插件的基礎(chǔ)服務(wù)。

用法

要使用 Java 插件,請(qǐng)?jiān)跇?gòu)建腳本中加入:

使用 Java 插件

build.gradle

apply plugin: 'java'  

源集

Java 插件引入了一個(gè)源集的概念。一個(gè)源集只是一組用于編譯并一起執(zhí)行的源文件。這些源文件可能包括 Java 源代碼文件和資源文件。其他有一些插件添加了在源集里包含 Groovy 和 Scala 的源代碼文件的能力。一個(gè)源集有一個(gè)相關(guān)聯(lián)的編譯類(lèi)路徑和運(yùn)行時(shí)類(lèi)路徑。

源集的一個(gè)用途是,把源文件進(jìn)行邏輯上的分組,以描述它們的目的。例如,你可能會(huì)使用一個(gè)源集來(lái)定義一個(gè)集成測(cè)試套件,或者你可能會(huì)使用單獨(dú)的源集來(lái)定義你的項(xiàng)目的 API 和實(shí)現(xiàn)類(lèi)。

Java 插件定義了兩個(gè)標(biāo)準(zhǔn)的源集,分別是 main 和 test。main 源集包含你產(chǎn)品的源代碼,它們將被編譯并組裝成一個(gè) JAR 文件。test 源集包含你的單元測(cè)試的源代碼,它們將被編譯并使用 JUnit 或 TestNG 來(lái)執(zhí)行。

任務(wù)

Java 插件向你的項(xiàng)目添加了大量的任務(wù),如下所示。

表 23.1. Java 插件-任務(wù)

任務(wù)名稱(chēng) 依賴(lài)于 類(lèi)型 描述
compileJava 產(chǎn)生編譯類(lèi)路徑中的所有任務(wù)。這包括了用于jar任務(wù)。 JavaCompile 使用 javac 編譯產(chǎn)品中的 Java 源文件。
processResources - Copy 把生產(chǎn)資源文件拷貝到生產(chǎn)的類(lèi)目錄中。
classes processResources。一些插件添加了額外的編譯任務(wù)。 Task 組裝生產(chǎn)的類(lèi)目錄。
compileTestJava compile,再加上所有能產(chǎn)生測(cè)試編譯類(lèi)路徑的任務(wù)。 JavaCompile 使用 javac 編譯 Java 的測(cè)試源文件。
processTestResources - Copy 把測(cè)試的資源文件拷貝到測(cè)試的類(lèi)目錄中。
testClasses processTestResources。一些插件添加了額外的測(cè)試編譯任務(wù)。 Task 組裝測(cè)試的類(lèi)目錄。
jar compile Jar 組裝 JAR 文件
javadoc compile Javadoc 使用 Javadoc 生成生產(chǎn)的 Java 源代碼的API文檔
test compileTest,再加上所有產(chǎn)生測(cè)試運(yùn)行時(shí)類(lèi)路徑的任務(wù)。 Test 使用 JUnit 或 TestNG運(yùn)行單元測(cè)試。
uploadArchives 使用jar。 Upload 使用archives配置上傳包括 JAR 文件的構(gòu)件。
clean - Delete 刪除項(xiàng)目的 build 目錄。
TaskName - Delete 刪除由指定的任務(wù)所產(chǎn)生的輸出文件。例如, jar任務(wù)中所創(chuàng)建的 JAR 文件,test任務(wù)所創(chuàng)建的測(cè)試結(jié)果。

對(duì)于每個(gè)你添加到該項(xiàng)目中的源集,Java 插件將添加以下的編譯任務(wù):

表 23.2. Java 插件-源集任務(wù)

任務(wù)名稱(chēng) 依賴(lài)于 類(lèi)型 描述
SourceSetJava 所有產(chǎn)生源集編譯類(lèi)路徑的任務(wù)。 JavaCompile 使用 javac 編譯給定的源集中的 Java 源文件。
SourceSetResources - Copy 把給定的源集的資源文件拷貝到類(lèi)目錄中。
sourceSetClasses SourceSetResources。某些插件還為源集添加了額外的編譯任務(wù)。 Task 組裝給定源集的類(lèi)目錄。

Java 插件還增加了大量的任務(wù)構(gòu)成該項(xiàng)目的生命周期:

表 23.3. Java 插件-生命周期任務(wù)

任務(wù)名稱(chēng) 依賴(lài)于 類(lèi)型 描述
assemble 項(xiàng)目中的所有歸檔項(xiàng)目,包括jar任務(wù)。某些插件還向項(xiàng)目添加額外的歸檔任務(wù)。 Task 組裝項(xiàng)目中所有的歸類(lèi)文件。
check 項(xiàng)目中的所有核查項(xiàng)目,包括test任務(wù)。某些插件還向項(xiàng)目添加額外的核查任務(wù)。 Task 執(zhí)行項(xiàng)目中所有的核查任務(wù)。
build assemble Task 執(zhí)行項(xiàng)目的完事構(gòu)建。
buildNeeded build任務(wù)。 Task 執(zhí)行項(xiàng)目本身及它所依賴(lài)的其他所有項(xiàng)目的完整構(gòu)建。
buildDependents build任務(wù)。 Task 執(zhí)行項(xiàng)目本身及依賴(lài)它的其他所有項(xiàng)目的完整構(gòu)建。
ConfigurationName 使用配置ConfigurationName生成構(gòu)件的任務(wù)。 Task 組裝指定配置的構(gòu)件。該任務(wù)由Base插件添加,并由Java插件隱式實(shí)現(xiàn)。
ConfigurationName 使用配置ConfigurationName上傳構(gòu)件的任務(wù)。 Upload 組裝并上傳指定配置的構(gòu)件。該任務(wù)由Base插件添加,并由Java插件隱式實(shí)現(xiàn)。

uploadConfigurationName 使用配置 ConfigurationName 上傳構(gòu)件的任務(wù)。 Upload 組裝并上傳指定配置的構(gòu)件。該任務(wù)由 Base 插件添加,并由 Java 插件隱式實(shí)現(xiàn)。 下圖顯示了這些任務(wù)之間的關(guān)系。

圖23.1. Java 插件 ??- 任務(wù)

Java 插件 ??- 任務(wù)

項(xiàng)目布局

Java 插件會(huì)假定如下所示的項(xiàng)目布局。這些目錄都不需要一定存在,或者是里面有什么內(nèi)容。Java 插件將會(huì)進(jìn)行編譯,不管它發(fā)現(xiàn)什么,并處理缺少的任何東西。

表 23.4. Java 插件-默認(rèn)項(xiàng)目布局

目錄 意義
src/main/java 產(chǎn)品的Java源代碼
src/main/resources 產(chǎn)品的資源
src/test/java Java 測(cè)試源代碼
src/test/resources 測(cè)試資源
sourceSet/java 給定的源集的Java源代碼
sourceSet/resources 給定的源集的資源

更改項(xiàng)目布局

你可以通過(guò)配置適當(dāng)?shù)脑醇?,?lái)配置項(xiàng)目的布局。這一點(diǎn)將在以下各節(jié)中詳細(xì)討論。這里是如何更改 main Java 和資源源目錄的一個(gè)簡(jiǎn)短的例子。

自定義 Java 源代碼布局

build.gradle

sourceSets {
    main {
        java {
            srcDir 'src/java'
        }
        resources {
            srcDir 'src/resources'
        }
    }
}  

依賴(lài)管理

Java 插件向項(xiàng)目添加了許多依賴(lài)配置,如下圖所示。它對(duì)一些任務(wù)指定了這些配置,如 compileJava 和 test。

表23.5. Java插件 ??- 依賴(lài)配置

名稱(chēng) 繼承自 在哪些任務(wù)中使用 意義
compile - compileJava 編譯時(shí)依賴(lài)
runtime compile - 運(yùn)行時(shí)依賴(lài)
testCompile compile compileTestJava 用于編譯測(cè)試的其他依賴(lài)
testRuntime runtime, testCompile test 只用于運(yùn)行測(cè)試的其他依賴(lài)
archives - uploadArchives 由本項(xiàng)目生產(chǎn)的構(gòu)件(如jar包)。
default runtime - 本項(xiàng)目上的默認(rèn)項(xiàng)目依賴(lài)配置。包含本項(xiàng)目運(yùn)行時(shí)所需要的構(gòu)件和依賴(lài)。

圖23.2. Java 插件 ??- 依賴(lài)配置

Java 插件 ??- 依賴(lài)配置

對(duì)于每個(gè)你添加到項(xiàng)目中的源集,Java 插件都會(huì)添加以下的依賴(lài)配置:

表23.6. Java 插件 ??- 源集依賴(lài)配置

名稱(chēng) 繼承自 在哪些任務(wù)中使用 意義
sourceSetCompile - compileSourceSetJava 給定源集的編譯時(shí)依賴(lài)
sourceSetRuntime sourceSetCompile - 給定源集的運(yùn)行時(shí)依賴(lài)

常規(guī)屬性

Java 插件向項(xiàng)目添加了許多常規(guī)屬性,如下圖所示。您可以在構(gòu)建腳本中使用這些屬性,就像它們是 project 對(duì)象的屬性一樣。

表23.7. Java 插件 ??- 目錄屬性

屬性名稱(chēng) 類(lèi)型 默認(rèn)值 描述
reportsDirName String reports 相對(duì)于build目錄的目錄名稱(chēng),報(bào)告將生成到此目錄。
reportsDir File (read-only) reportsDirName 報(bào)告將生成到此目錄。
testResultsDirName String test-results 相對(duì)于build目錄的目錄名稱(chēng),測(cè)試報(bào)告的.xml文件將生成到此目錄。
testResultsDir File (read-only) testResultsDirName 測(cè)試報(bào)告的.xml文件將生成到此目錄。
testReportDirName String tests 相對(duì)于build目錄的目錄名稱(chēng),測(cè)試報(bào)告將生成到此目錄。
testReportDir File (read-only) testReportDirName 測(cè)試報(bào)告生成到此目錄。
libsDirName String libs 相對(duì)于build目錄的目錄名稱(chēng),類(lèi)庫(kù)將生成到此目錄中。
libsDir File (read-only) libsDirName 類(lèi)庫(kù)將生成到此目錄中。
distsDirName String distributions 相對(duì)于build目錄的目錄名稱(chēng),發(fā)布的文件將生成到此目錄中。
distsDir File (read-only) distsDirName 要發(fā)布的文件將生成到此目錄。
docsDirName String docs 相對(duì)于build目錄的目錄名稱(chēng),文檔將生成到此目錄。
docsDir File (read-only) docsDirName 要生成文檔的目錄。
dependencyCacheDirName String dependency-cache 相對(duì)于build目錄的目錄名稱(chēng),該目錄用于緩存源代碼的依賴(lài)信息。
dependencyCacheDir File (read-only) dependencyCacheDirName 該目錄用于緩存源代碼的依賴(lài)信息。

表 23.8. Java 插件 - 其他屬性

屬性名稱(chēng) 類(lèi)型 默認(rèn)值 描述
sourceSets SourceSetContainer (read-only) 非空 包含項(xiàng)目的源集。
sourceCompatibility JavaVersion. 可以使用字符串或數(shù)字來(lái)設(shè)置,例如1.5。 當(dāng)前JVM所使用的值 當(dāng)編譯Java源代碼時(shí)所使用的Java版本兼容性。
targetCompatibility JavaVersion. 可以使用字符串或數(shù)字來(lái)設(shè)置,例如1.5。 sourceCompatibility 要生成的類(lèi)的 Java 版本。
archivesBaseName String projectName 像JAR或ZIP文件這樣的構(gòu)件的basename
manifest Manifest 一個(gè)空的清單 要包括的所有 JAR 文件的清單。

這些屬性由 JavaPluginConvention, BasePluginConvention 和 ReportingBasePluginConvention 這些類(lèi)型的常規(guī)對(duì)象提供。

使用源集

你可以使用 sourceSets 屬性訪(fǎng)問(wèn)項(xiàng)目的源集。這是項(xiàng)目的源集的容器,它的類(lèi)型是 SourceSetContainer。除此之后,還有一個(gè) sourceSets {}的腳本塊,可以傳入一個(gè)閉包來(lái)配置源集容器。源集容器的使用方式幾乎與其他容器一樣,例如 tasks。

訪(fǎng)問(wèn)源集

build.gradle

// Various ways to access the main source set
println sourceSets.main.output.classesDir
println sourceSets['main'].output.classesDir
sourceSets {
    println main.output.classesDir
}
sourceSets {
    main {
        println output.classesDir
    }
}
// Iterate over the source sets
sourceSets.all {
    println name
}  

要配置一個(gè)現(xiàn)有的源集,你只需使用上面的其中一種訪(fǎng)問(wèn)方法來(lái)設(shè)置源集的屬性。這些屬性將在下文中進(jìn)行介紹。下面是一個(gè)配置 main 的 Java 和資源目錄的例子:

配置源集的源代碼目錄

build.gradle

sourceSets {
    main {
        java {
            srcDir 'src/java'
        }
        resources {
            srcDir 'src/resources'
        }
    }
}  

源集屬性

下表列出了一些重要的源集屬性。你可以在 SourceSet 的 API 文檔中查看更多的詳細(xì)信息。

表 23.9. Java 插件 - 源集屬性

屬性名稱(chēng) 類(lèi)型 默認(rèn)值 描述
name String (read-only) 非空 用來(lái)確定一個(gè)源集的源集名稱(chēng)。
output SourceSetOutput (read-only) 非空 源集的輸出文件,包含它編譯過(guò)的類(lèi)和資源。
output.classesDir File name 要生成的該源集的類(lèi)的目錄。
output.resourcesDir File name 要生成的該源集的資源的目錄。
compileClasspath FileCollection SourceSet 配置。 該類(lèi)路徑在編譯該源集的源文件時(shí)使用。
runtimeClasspath FileCollection SourceSet 配置。 該類(lèi)路徑在執(zhí)行該源集的類(lèi)時(shí)使用。
java SourceDirectorySet (read-only) 非空 該源集的Java源文件。僅包含Java源文件目錄里的.java文件,并排除其他所有文件。
java.srcDirs Set<File> name/java] 該源目錄包含了此源集的所有Java源文件。
resources SourceDirectorySet (read-only) 非空 此源集的資源文件。僅包含資源文件,并且排除在資源源目錄中找到的所有 .java文件。其他插件,如Groovy 插件,會(huì)從該集合中排除其他類(lèi)型的文件。
resources.srcDirs Set<File> name/resources] 該源目錄包含了此源集的資源文件。
allJava SourceDirectorySet (read-only) java 該源集的所有.java 文件。有些插件,如Groovy 插件,會(huì)從該集合中增加其他的Java源文件。
allSource SourceDirectorySet (read-only) resources + java 該源集的所有源文件。包含所有的資源文件和Java源文件。有些插件,如Groovy 插件,會(huì)從該集合中增加其他的源文件。

定義新的源集

要定義一個(gè)新的源集,你只需在 sourceSets {}塊中引用它。下面是一個(gè)示例:

定義一個(gè)源集

build.gradle

sourceSets {
    intTest
}  

當(dāng)您定義一個(gè)新的源集時(shí),Java 插件會(huì)為該源集添加一些依賴(lài)配置,如表 23.6,“Java 插件 - 源集依賴(lài)項(xiàng)配置”所示。你可以使用這些配置來(lái)定義源集的編譯和運(yùn)行時(shí)的依賴(lài)。

定義源集依賴(lài)

build.gradle

sourceSets {
    intTest
}
dependencies {
    intTestCompile 'junit:junit:4.11'
    intTestRuntime 'org.ow2.asm:asm-all:4.0'
}  

Java 插件還添加了大量的任務(wù),用于組裝源集的類(lèi),如表 23.2,“Java 插件 - 源設(shè)置任務(wù)”所示。例如,對(duì)于一個(gè)被叫做 intTest 的源集,你可以運(yùn)行 gradle intTestClasses 來(lái)編譯 int 測(cè)試類(lèi)。

編譯源集

gradle intTestClasses的輸出結(jié)果

> gradle intTestClasses
:compileIntTestJava
:processIntTestResources
:intTestClasses
BUILD SUCCESSFUL
Total time: 1 secs  

一些源集的范例

添加一個(gè)包含了源集的類(lèi)的 JAR 包

示例 23.8. 為一個(gè)源集裝配一個(gè)JAR文件

build.gradle

task intTestJar(type: Jar) {
    from sourceSets.intTest.output
}  

為一個(gè)源集生成 Javadoc:

示例 23.9. 為一個(gè)源集生成 Javadoc:

build.gradle

task intTestJavadoc(type: Javadoc) {
    source sourceSets.intTest.allJava
}  

添加一個(gè)測(cè)試套件以運(yùn)行一個(gè)源集里的測(cè)試

示例 23.10. 運(yùn)行源集里的測(cè)試

build.gradle

task intTest(type: Test) {
    testClassesDir = sourceSets.intTest.output.classesDir
    classpath = sourceSets.intTest.runtimeClasspath
}  

Javadoc

Javadoc 任務(wù)是 Javadoc 的一個(gè)實(shí)例。它支持核心的 javadoc 參數(shù)選項(xiàng),以及在 Javadoc 可執(zhí)行文件的參考文檔中描述的標(biāo)準(zhǔn) doclet 參數(shù)選項(xiàng)。對(duì)于支持的 Javadoc 參數(shù)選項(xiàng)的完整列表,請(qǐng)參考下面的類(lèi)的 API 文檔: CoreJavadocOptions 和StandardJavadocDocletOptions。

表 23.10. Java 插件 - Javadoc 屬性

任務(wù)屬性 類(lèi)型 默認(rèn)值
classpath FileCollection sourceSets.main.output + sourceSets.main.compileClasspath
source FileTree. sourceSets.main.allJava
destinationDir File docsDir/javadoc
title String project的名稱(chēng)和版本

清理

clean 任務(wù)是 Delete 的一個(gè)實(shí)例。它只是刪除由其 dir 屬性表示的目錄。

表 23.11. Java 插件 - Clean 性能

任務(wù)屬性 類(lèi)型 默認(rèn)值
dir File buildDir

資源

Java 插件使用 Copy 任務(wù)進(jìn)行資源的處理。它為該 project 里的每個(gè)源集添加一個(gè)實(shí)例。你可以在16.6章節(jié),“復(fù)制文件”中找到關(guān)于 copy 任務(wù)的更多信息。

表 23.12. Java 插件-ProcessResources 屬性

任務(wù)屬性 類(lèi)型 默認(rèn)值
srcDirs Object. sourceSet.resources
destinationDir  16.1 章節(jié),“查找文件”中所講到的任何一種方式來(lái)設(shè)置。 sourceSet.output.resourcesDir

CompileJava

Java 插件為該 project 里的每個(gè)源集添加一個(gè) JavaCompile 實(shí)例。一些最常見(jiàn)的配置選項(xiàng)如下所示。

表 23.13. Java 插件- Compile 屬性

任務(wù)屬性 類(lèi)型 默認(rèn)值
classpath FileCollection sourceSet.compileClasspath
source FileTree sourceSet.java
destinationDir File. sourceSet.output.classesDir

compile 任務(wù)委派了 Ant 的 javac 任務(wù)。將 options.useAnt 設(shè)置為 false 將繞過(guò) Ant 任務(wù),而激活 Gradle 的直接編譯器集成。在未來(lái)的 Gradle 版本中,將把它作為默認(rèn)設(shè)置。

默認(rèn)情況下,Java 編譯器在 Gradle 過(guò)程中運(yùn)行。將 options.fork 設(shè)置為 true 將會(huì)使編譯出現(xiàn)在一個(gè)單獨(dú)的進(jìn)程中。在 Ant javac 任務(wù)中,這意味著將會(huì)為每一個(gè) compile 任務(wù)fork 一個(gè)新的進(jìn)程,而這將會(huì)使編譯變慢。相反,Gradle 的直接編譯器集成 (見(jiàn)上文) 將盡可能多地重用相同的編譯器進(jìn)程。在這兩種情況下,使用 options.forkOptions 指定的所有fork 選項(xiàng)都將得到重視。

Test

test 任務(wù)是 Test 的一個(gè)實(shí)例。它會(huì)自動(dòng)檢測(cè)和執(zhí)行 test 源集中的所有單元測(cè)試。測(cè)試執(zhí)行完成后,它還會(huì)生成一份報(bào)告。同時(shí)支持 JUnit 和 TestNG。可以看一看Test的完整的 API。

測(cè)試執(zhí)行

測(cè)試在單獨(dú)的 JVM 中執(zhí)行,與 main 構(gòu)建進(jìn)程隔離。Test 任務(wù)的 API 可以讓你控制什么時(shí)候開(kāi)始。

有大量的屬性用于控制測(cè)試進(jìn)程的啟動(dòng)。這包括系統(tǒng)屬性、 JVM 參數(shù)和使用的 Java 可執(zhí)行文件。

你可以指定是否要并行運(yùn)行你的測(cè)試。Gradle 通過(guò)同時(shí)運(yùn)行多個(gè)測(cè)試進(jìn)程來(lái)提供并行測(cè)試的執(zhí)行。每個(gè)測(cè)試進(jìn)程會(huì)依次執(zhí)行一個(gè)單一的測(cè)試,所以你一般不需要對(duì)你的測(cè)試做任何的配置來(lái)利用這一點(diǎn)。 MaxParallelForks 屬性指定在給定的時(shí)間可以運(yùn)行的測(cè)試進(jìn)程的最大數(shù)。它的默認(rèn)值是 1,也就是說(shuō),不并行執(zhí)行測(cè)試。

測(cè)試進(jìn)程程會(huì)為其將 org.gradle.test.worker 系統(tǒng)屬性設(shè)置為一個(gè)唯一標(biāo)識(shí)符,這個(gè)標(biāo)識(shí)符可以用于文件名稱(chēng)或其他資源標(biāo)識(shí)符。

你可以指定在執(zhí)行了一定數(shù)量的測(cè)試類(lèi)之后,重啟那個(gè)測(cè)試進(jìn)程。這是一個(gè)很有用的替代方案,讓你的測(cè)試進(jìn)程可以有很大的堆內(nèi)存。forkEvery 屬性指定了要在測(cè)試進(jìn)程中執(zhí)行的測(cè)試類(lèi)的最大數(shù)。默認(rèn)是每個(gè)測(cè)試進(jìn)程中執(zhí)行的測(cè)試數(shù)量不限。

該任務(wù)有一個(gè) ignoreFailures 屬性,用以控制測(cè)試失敗時(shí)的行為。test 會(huì)始終執(zhí)行它檢測(cè)到的每一個(gè)測(cè)試。如果 ignoreFailures 為 false,并且有測(cè)試不通過(guò),那它會(huì)停止繼續(xù)構(gòu)建。IgnoreFailures 的默認(rèn)值為 false。

testLogging 屬性可以配置哪些測(cè)試事件需要記錄,并且使用什么樣的日志級(jí)別。默認(rèn)情況下,對(duì)于每個(gè)失敗的測(cè)試都只會(huì)打印一個(gè)簡(jiǎn)潔的消息。請(qǐng)參閱 TestLoggingContainer,查看如何把你的測(cè)試日志打印調(diào)整為你的偏好設(shè)置。

調(diào)試

test 任務(wù)提供了一個(gè) Test.getDebug()屬性,可以設(shè)置為啟動(dòng),使 JVM 在執(zhí)行測(cè)試之前,等待一個(gè) debugger 連接到它的 5005 端口上。

這也可以在調(diào)用時(shí)通過(guò)--debug-vm task 選項(xiàng)進(jìn)行啟用。

測(cè)試過(guò)濾

從 Gradle 1.10 開(kāi)始,可以根據(jù)測(cè)試的名稱(chēng)模式,只包含指定的測(cè)試。過(guò)濾,相對(duì)于測(cè)試類(lèi)的包含或排除,是一個(gè)不同的機(jī)制。它將在接下來(lái)的段落中進(jìn)行描述(-Dtest.single, test.include 和 friends)。后者基于文件,比如測(cè)試實(shí)現(xiàn)類(lèi)的物理位置。file-level 的測(cè)試選擇不支持的很多有趣的情況,都可以用 test-level 過(guò)濾來(lái)做到。以下這些場(chǎng)景中,有一些 Gradle 現(xiàn)在就可以處理,而有一些則將在未來(lái)得到實(shí)現(xiàn):

  • 在指定的測(cè)試的方法級(jí)別上進(jìn)行過(guò)濾;執(zhí)行單個(gè)測(cè)試方法
  • 基于自定義注解(以后實(shí)現(xiàn))進(jìn)行過(guò)濾
  • 基于測(cè)試層次結(jié)構(gòu)進(jìn)行過(guò)濾 ;執(zhí)行所有繼承了某一基類(lèi)(以后實(shí)現(xiàn)) 的測(cè)試
  • 基于一些自定義的運(yùn)行時(shí)的規(guī)則進(jìn)行過(guò)濾,例如某個(gè)系統(tǒng)屬性或一些靜態(tài)的特定值(以后實(shí)現(xiàn))

測(cè)試過(guò)濾功能具有以下的特征:

  • 支持完整的限定類(lèi)名稱(chēng)或完整的限定的方法名稱(chēng),例如“org.gradle.SomeTest”、“org.gradle.SomeTest.someMethod”
  • 通配符 “*” 支付匹配任何字符
  • 提供了“--tests”的命令行選項(xiàng),以方便地設(shè)置測(cè)試過(guò)濾器。這對(duì)“單一測(cè)試方法的執(zhí)行”的經(jīng)典用例特別有用。當(dāng)使用該命令行選項(xiàng)選項(xiàng)時(shí),在構(gòu)建腳本中聲明的列入過(guò)濾器的測(cè)試將會(huì)被忽略。
  • Gradle 盡最大努力對(duì)有著特定的測(cè)試框架 API 的局限的測(cè)試進(jìn)行過(guò)濾。一些高級(jí)的、 綜合的測(cè)試可能不完全符合過(guò)濾條件。然而,絕大多數(shù)的測(cè)試和用例都會(huì)被很好地處理。
  • 測(cè)試過(guò)濾將會(huì)取代基于文件的測(cè)試選擇。后者可能在將來(lái)會(huì)被完全地取代掉。我們將會(huì)繼續(xù)改進(jìn)測(cè)試過(guò)濾的 API,并添加更多種類(lèi)的過(guò)濾器。

在構(gòu)建腳本中過(guò)濾測(cè)試

build.gradle

test {
    filter {
        //include specific method in any of the tests
        includeTestsMatching "*UiCheck"
        //include all tests from package
        includeTestsMatching "org.gradle.internal.*"
        //include all integration tests
        includeTestsMatching "*IntegTest"
    }
}  

有關(guān)更多的詳細(xì)信息和示例,請(qǐng)參閱 TestFilter 的文檔。

使用命令行選項(xiàng)的一些示例:

  • gradle test --tests org.gradle.SomeTest.someSpecificFeature
  • gradle test --tests *SomeTest.someSpecificFeature
  • gradle test --tests *SomeSpecificTest
  • gradle test --tests all.in.specific.package*
  • gradle test --tests *IntegTest
  • gradle test --tests IntegTestui*
  • gradle someTestTask --tests UiTest someOtherTestTask --tests WebTest*ui

通過(guò)系統(tǒng)屬性執(zhí)行單一的測(cè)試

這種機(jī)制已經(jīng)被上述的“測(cè)試過(guò)濾”所取代。 設(shè)置一個(gè) taskName.single = testNamePattern 的系統(tǒng)屬性將會(huì)只執(zhí)行匹配 testNamePattern 的那些測(cè)試。這個(gè) taskName 可以是一個(gè)完整的多項(xiàng)目路徑,比如“sub1:sub2:test”,或者僅是一個(gè)任務(wù)名稱(chēng)。testNamePattern 將用于形成一個(gè)“*/testNamePattern.class” 的包含模式。如果這個(gè)模式無(wú)法找到任何測(cè)試,那么將會(huì)拋出一個(gè)異常。這是為了使你不會(huì)誤以為測(cè)試通過(guò)。如果執(zhí)行了一個(gè)以上的子項(xiàng)目的測(cè)試,該模式會(huì)被應(yīng)用于每一個(gè)子項(xiàng)目中。如果在一個(gè)特定的子項(xiàng)目中,找不到測(cè)試用例,那么將會(huì)拋出異常。在這種情況下你可以使用路徑標(biāo)記法的模式,這樣該模式就會(huì)只應(yīng)用于特定的子項(xiàng)目的測(cè)試任務(wù)中?;蛘吣憧梢灾付ㄒ獔?zhí)行的任務(wù)的完整限定名稱(chēng)。你還可以指定多個(gè)模式。示例:

  • gradle -Dtest.single=ThisUniquelyNamedTest test
  • gradle -Dtest.single=a/b/ test
  • gradle -DintegTest.single=*IntegrationTest integTest
  • gradle -Dtest.single=:proj1:test:Customer build
  • gradle -DintegTest.single=c/d/ :proj1:integTest

測(cè)試檢測(cè)

Test 任務(wù)通過(guò)檢查編譯過(guò)的測(cè)試類(lèi)來(lái)檢測(cè)哪些類(lèi)是測(cè)試類(lèi)。默認(rèn)情況下它會(huì)掃描所有的.class 文件。您可以設(shè)置自定義的 includes 或 excludes,這樣就只有這些類(lèi)才會(huì)被掃描。根據(jù)所使用的測(cè)試框架 (JUnit 或 TestNG),會(huì)使用不同的標(biāo)準(zhǔn)進(jìn)行測(cè)試類(lèi)的檢測(cè)。

當(dāng)使用 JUnit 時(shí),我們掃描 JUnit 3 和 4 的測(cè)試類(lèi)。如果滿(mǎn)足以下的任何一個(gè)條件,這個(gè)類(lèi)就被認(rèn)為是一個(gè) JUnit 測(cè)試類(lèi):

  • 類(lèi)或超類(lèi)繼承自 TestCase 或 GroovyTestCase
  • 類(lèi)或超類(lèi)使用了 @RunWith 進(jìn)行注解
  • 類(lèi)或超類(lèi)含有一個(gè)帶 @Test 注解的方法

當(dāng)使用 TestNG 時(shí),我們掃描所有帶有 @Test 注解的方法。

請(qǐng)注意,抽象類(lèi)不會(huì)被執(zhí)行。Gradle 還將掃描測(cè)試類(lèi)路徑中的 jar 文件里的繼承樹(shù)。

如果你不想要使用測(cè)試類(lèi)檢測(cè),可以通過(guò)設(shè)置 scanForTestClasses 為 false 來(lái)禁用它。這將使test任務(wù)只使用 includes / excludes 來(lái)找到測(cè)試類(lèi)。如果 scanForTestClasses 為disabled,并且沒(méi)有指定 include 或 exclude 模式,則使用各自的默認(rèn)值。對(duì)于 include 的默認(rèn)值是 "/*Tests.class", "*/Test.class",而對(duì)于exclude它的默認(rèn)值是 "/Abstract*.class"。

測(cè)試分組

JUnit 和 TestNG 可以對(duì)測(cè)試方法進(jìn)行復(fù)雜的分組。

為對(duì) Junit 測(cè)試類(lèi)和方法進(jìn)行分組,JUnit 4.8 引入了類(lèi)別的概念。test 任務(wù)可以實(shí)現(xiàn)一個(gè)規(guī)范,讓你 include 和 exclude 想要的 JUnit 類(lèi)別。

JUnit 類(lèi)別

build.gradle

test {
    useJUnit {
        includeCategories 'org.gradle.junit.CategoryA'
        excludeCategories 'org.gradle.junit.CategoryB'
    }
}  

TestNG 框架有一個(gè)非常相似的概念。在 TestNG 中你可以指定不同的測(cè)試組。應(yīng)從測(cè)試執(zhí)行中 include 或 exclude 的測(cè)試組,可以在 test 任務(wù)中配置。

對(duì) TestNG 測(cè)試分組

build.gradle

test {
    useTestNG {
        excludeGroups 'integrationTests'
        includeGroups 'unitTests'
    }
}  

測(cè)試報(bào)告

test 任務(wù)默認(rèn)情況下會(huì)生成以下結(jié)果。

  • 一個(gè) HTML 測(cè)試報(bào)告。
  • 與 Ant Junit report 任務(wù)兼容的 XML 格式的結(jié)果。這種格式可以被許多工具所支持,比如CI服務(wù)器。
  • 有效二進(jìn)制格式的結(jié)果。這個(gè)任務(wù)會(huì)從這些二進(jìn)制結(jié)果生成其他的結(jié)果。

您可以使用 Test.setTestReport()方法來(lái)禁用 HTML 測(cè)試報(bào)告。目前不能禁用其他的結(jié)果。

這里還有一個(gè)獨(dú)立的 TestReport 任務(wù)類(lèi)型,它可以從一個(gè)或多個(gè) Test 任務(wù)實(shí)例生成的二進(jìn)制結(jié)果中生成 HTML 測(cè)試報(bào)告。要使用這個(gè)任務(wù)類(lèi)型,你需要定義一個(gè) destinationDir 和要包含到報(bào)告的測(cè)試結(jié)果。這里是一個(gè)范例,從子項(xiàng)目的單元測(cè)試中生成一個(gè)聯(lián)合報(bào)告:

為多個(gè)子項(xiàng)目創(chuàng)建一個(gè)單元測(cè)試報(bào)告

build.gradle

subprojects {
    apply plugin: 'java'

    // Disable the test report for the individual test task
    test {
        reports.html.enabled = false
    }
}
task testReport(type: TestReport) {
    destinationDir = file("$buildDir/reports/allTests")
    // Include the results from the `test` task in all subprojects
    reportOn subprojects*.test
}  

你應(yīng)該注意到,TestReport 類(lèi)型組合了多個(gè)測(cè)試任務(wù)的結(jié)果,并且需要聚合各個(gè)測(cè)試類(lèi)的結(jié)果。這意味著,如果一個(gè)給定的測(cè)試類(lèi)被多個(gè) test 任務(wù)所執(zhí)行,那么測(cè)試報(bào)告將包括那個(gè)類(lèi)的所有執(zhí)行結(jié)果,但它難以區(qū)分那個(gè)類(lèi)的每次執(zhí)行和它們的輸出。

TestNG 參數(shù)化方法和報(bào)告

TestNG 支持參數(shù)化測(cè)試方法,允許一個(gè)特定的測(cè)試方法使用不同的輸入執(zhí)行多次。Gradle 會(huì)在這個(gè)方法的執(zhí)行報(bào)告中包含進(jìn)它的參數(shù)值。

給定一個(gè)帶有兩個(gè)參數(shù),名為 aParameterizedTestMethod 參數(shù)化測(cè)試方法,它將使用名稱(chēng)這個(gè)名稱(chēng)進(jìn)行報(bào)告 :aParameterizedTestMethod(toStringValueOfParam1, toStringValueOfParam2)。這使得在特定的迭代過(guò)程,很容易確定參數(shù)值。

常規(guī)值

表 23.14. Java 插件 - test 屬性

任務(wù)屬性 類(lèi)型 默認(rèn)值
testClassesDir File sourceSets.test.output.classesDir
classpath FileCollection sourceSets.test.runtimeClasspath
testResultsDir File testResultsDir
testReportDir File testReportDir
testSrcDirs List<File> sourceSets.test.java.srcDirs

Jar

Jar 任務(wù)創(chuàng)建一個(gè)包含類(lèi)文件和項(xiàng)目資源的 JAR 文件。JAR 文件在 archives 依賴(lài)配置中被聲明為一個(gè)構(gòu)件。這意味著這個(gè) JAR 文件被包含在一個(gè)依賴(lài)它的項(xiàng)目的類(lèi)路徑中。如果你把你的項(xiàng)目上傳到倉(cāng)庫(kù),這個(gè) JAR 文件會(huì)被聲明為依賴(lài)描述符的一部分。你可以在第16.8節(jié),“創(chuàng)建檔案”和第51章, 發(fā)布 artifact 中了解如何使用 archives 和配置 artifact。

Manifest

每個(gè) jar 或 war 對(duì)象都有一個(gè)單獨(dú)的 Manifest 實(shí)例的 manifest 屬性。當(dāng)生成 archive 時(shí),相應(yīng)的 MANIFEST.MF 文件也會(huì)被寫(xiě)入進(jìn)去。

自定義的 MANIFEST.MF

build.gradle

jar {
    manifest {
        attributes("Implementation-Title": "Gradle", "Implementation-Version": version)
    }
}  

您可以創(chuàng)建一個(gè)單獨(dú)的 Manifest 實(shí)例。它可以用于共享兩個(gè) jar 包的 manifest 信息。

創(chuàng)建一個(gè) manifest 對(duì)象。

build.gradle

ext.sharedManifest = manifest {
    attributes("Implementation-Title": "Gradle", "Implementation-Version": version)
}
task fooJar(type: Jar) {
    manifest = project.manifest {
        from sharedManifest
    }
}  

你可以把其他的 manifest 合并到任何一個(gè) Manifest 對(duì)象中。其他的 manifest 可能使用文件路徑來(lái)描述,像上面的例子,使用對(duì)另一個(gè) Manifest 對(duì)象的引用。

指定 archive 的單獨(dú) MANIFEST.MF

build.gradle

task barJar(type: Jar) {
    manifest {
        attributes key1: 'value1'
        from sharedManifest, 'src/config/basemanifest.txt'
        from('src/config/javabasemanifest.txt', 'src/config/libbasemanifest.txt') {
            eachEntry { details ->
                if (details.baseValue != details.mergeValue) {
                    details.value = baseValue
                }
                if (details.key == 'foo') {
                    details.exclude()
                }
            }
        }
    }
}  

Manifest 會(huì)根據(jù)在 from 語(yǔ)句中所聲明的順序進(jìn)行合并。如果基本的 manifest 和要合并的 manifest 都定義了同一個(gè) key 的值,那么默認(rèn)情況下會(huì)采用要合并的 manifest 的值。你可以通過(guò)添加 eachEntry action 來(lái)完全自定義合并行為,它可以讓你對(duì)每一項(xiàng)生成的 manifest 訪(fǎng)問(wèn)它的一個(gè) ManifestMergeDetails 實(shí)例。這個(gè)合并操作不會(huì)在 from 語(yǔ)句中就馬上被觸發(fā)。它是懶執(zhí)行的,不管是對(duì)于生成一個(gè) jar 包,還是調(diào)用了 writeTo 或者 effectiveManifest

你可以很輕松地把一個(gè) manifest 寫(xiě)入磁盤(pán)中。

指定 archive 的單獨(dú) MANIFEST.MF

build.gradle

jar.manifest.writeTo("$buildDir/mymanifest.mf")  
以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)