Jacoco 和单元测试代码覆盖率 android-gradle-plugin >= 1.1
Jacoco and Unit Tests Code Coverage with android-gradle-plugin >= 1.1
我最近开始在我的一个项目中集成 android-gradle-plugin
1.1.0。该项目使用 robolectric 2.4
到 运行 单元测试。
这是一个多模块项目,依赖关系非常复杂(一些模块依赖于其他模块)。类似的东西:
--> application-module (dependsOn: module1, module2, module-core)
--> module1 (dependsOn: module-core)
--> module2 (dependsOn: module-core)
--> module-core (dependsOn: module3, module4)
--> module3 (library dependencies)
--> module4 (library dependencies)
更清晰的图片请查看jacoco-example项目。
我试图集成 JaCoCo 来为单元测试生成报告,但在我看来它 运行 只是 androidTests
基本上是仪器测试。
经过一些 google'ing 我在 GitHub 和其他文章中遇到了一些项目,但它们主要集中在 android-gradle-plugin
的早期版本或正在使用其他第三方插件,例如 android-unit-test
for example here.
可能是我失去了 google 的能力。但是有人可以指出我可以找到一些关于 android gradle 插件中的新内容以及如何 运行 仅用于单元测试的 jacoco 任务的文档的方向吗?
更新
采用了 nenick's example 中的脚本:
apply plugin: "jacoco"
configurations {
jacocoReport
}
task jacocoReport(dependsOn: 'testDebug') << {
ant {
taskdef(name:'jacocoreport',
classname: 'org.jacoco.ant.ReportTask',
classpath: configurations.jacocoReport.asPath)
mkdir dir: "${buildDir}/test-coverage-report"
mkdir dir: "${buildDir}/reports/jacoco/test/"
jacocoreport {
executiondata = files("${buildDir}/jacoco/testDebug.exec")
structure(name: "${rootProject.name}") {
classfiles {
fileset (dir: "${buildDir}/intermediates/classes/debug") {
//exclude(name: '**/*_*.class')
exclude(name: '**/R.class')
exclude(name: '**/R$*.class')
exclude(name: '**/BuildConfig.class')
}
}
sourcefiles {
fileset dir: "src/main/java"
fileset dir: "${buildDir}/generated/source/buildConfig/debug"
fileset dir: "${buildDir}/generated/source/r/debug"
}
}
xml destfile: "${buildDir}/reports/jacoco/test/jacocoTestReport.xml"
html destdir: "${buildDir}/test-coverage-report/"
}
}
}
dependencies {
jacocoReport 'org.jacoco:org.jacoco.ant:0.7.2.201409121644'
}
之后 ./gradlew jacocoReport
执行并生成报告,但它显示 0(零)测试覆盖率,这是不可能的,因为至少有一半 类 被测试。
UPDATE_2
尝试了这个 example。将下一个任务添加到我的 gradle 个构建文件中:
task jacocoTestReport(type:JacocoReport, dependsOn: "testDebug") {
group = "Reporting"
description = "Generate Jacoco coverage reports"
classDirectories = fileTree(
dir: "${buildDir}/intermediates/classes/debug",
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/BuildConfig.*',
'**/Manifest*.*']
)
sourceDirectories = files("${buildDir.parent}/src/main/java")
additionalSourceDirs = files([
"${buildDir}/generated/source/buildConfig/debug",
"${buildDir}/generated/source/r/debug"
])
executionData = files("${buildDir}/jacoco/testDebug.exec")
reports {
xml.enabled = true
html.enabled = true
}
}
同样的问题,生成了报告,但代码覆盖率仍然为零。
UPDATE_3
它接缝来自 UPDATE_2 的任务有效但仅适用于具有 apply plugin: 'com.android.application'
的模块(正确生成的报告)。但是对于 android 库 (apply plugin: 'com.android.library'
) 的模块,报告显示覆盖率为零,尽管模块包含的测试比应用程序模块多。
UPDATE_4
创建了一个简单的示例项目来演示我的问题。当前,如果您 运行 ./gradlew jacocoReport
会生成报告,但不会显示模块项目的测试覆盖率。看到这个 link
简短说明:当测试是 AndroidUnitTests(whiteout JUnit 4 和 Robolectric)时,JaCoCo 报告显示了所有模块的覆盖率。
有什么想法吗?
警告:这是黑客攻击!使用上面的配置,我整理了一个 hack,根据选择的构建任务在应用程序和库之间切换 android 插件。这对我来说效果很好,因为我最终不会在设置了应用程序模式的情况下提交代码。
// dynamically change the android plugin to application if we are running unit tests or test reports.
project.ext.androidPlugin = 'com.android.library'
for (String taskName : project.gradle.startParameter.taskNames) {
if (taskName.contains('UnitTest') || taskName.contains('jacocoTestReport')) {
project.ext.androidPlugin = 'com.android.application'
break
}
}
logger.lifecycle("Setting android pluging to ${project.ext.androidPlugin}")
apply plugin: project.ext.androidPlugin
...
apply plugin: 'jacoco'
configurations {
jacocoReport
}
task jacocoTestReport(type:JacocoReport, dependsOn: "testDebug") {
group = "Reporting"
description = "Generate Jacoco coverage reports"
classDirectories = fileTree(
dir: "${buildDir}/intermediates/classes/debug",
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/BuildConfig.*',
'**/Manifest*.*']
)
sourceDirectories = files("${buildDir.parent}/src/main/java")
additionalSourceDirs = files([
"${buildDir}/generated/source/buildConfig/debug",
"${buildDir}/generated/source/r/debug"
])
executionData = files("${buildDir}/jacoco/testDebug.exec")
reports {
xml.enabled = true
html.enabled = true
}
}
希望 android 工具团队尽快修复此问题。
请创建一个例子,我可以看看。我猜这是一些缺少的路径配置。
- 包含所有覆盖率文件 (*.exec)
- 添加所有源路径(module/src/main/java)
- 添加所有 class 路径 (module/build/intermediates/classes/debug)
这里有两个例子它看起来如何
我解决了 JaCoCo 的问题并使其与最新的 gradle android 插件 1.1.3
一起工作
具有最新 gradle 脚本的项目:https://github.com/OleksandrKucherenko/meter
References:
How to attach own implementation instead of Mocks in Android Studio Unit Tests?
https://plus.google.com/117981280628062796190/posts/8jWV22mnqUB
Small hint for everyone who try to use JaCoCo coverage in android builds... unexpected finding!!!
https://plus.google.com/117981280628062796190/posts/RreU44qmeuP
JaCoCo XML/HTML Report for Unit Tests https://plus.google.com/u/0/+OleksandrKucherenko/posts/6vNWkkLed3b
我终于能够使用 Android Studio 1.1 查看 JUnit 测试的代码覆盖率。
jacoco.gradle
apply plugin: 'jacoco'
jacoco {
toolVersion "0.7.1.201405082137"
}
def coverageSourceDirs = [
"$projectDir/src/main/java",
]
task jacocoTestReport(type: JacocoReport, dependsOn: "testDebug") {
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
reports {
xml.enabled = true
html.enabled = true
}
classDirectories = fileTree(
dir: './build/intermediates/classes/debug',
excludes: ['**/R*.class',
'**/*$InjectAdapter.class',
'**/*$ModuleAdapter.class',
'**/*$ViewInjector*.class'
]
)
sourceDirectories = files(coverageSourceDirs)
executionData = files("$buildDir/jacoco/testDebug.exec")
// Bit hacky but fixes https://code.google.com/p/android/issues/detail?id=69174.
// We iterate through the compiled .class tree and rename $$ to $.
doFirst {
new File("$buildDir/intermediates/classes/").eachFileRecurse { file ->
if (file.name.contains('$$')) {
file.renameTo(file.path.replace('$$', '$'))
}
}
}
}
然后在模块的 build.gradle 文件中(我把它放在 android
和 dependencies
之间):
apply from: '../jacoco.gradle'
也在 android
的 defaultConfig
块中。我添加了这个(不知道是否有必要,但我从 this blog 那里得到了这个):
android {
defaultConfig {
testHandleProfiling true
testFunctionalTest true
}
}
尽情享受吧。
我遇到了和你一样的问题。今天我确实完全删除了 android studio,android sdk,gradle。然后重新安装一切。在那之后,我只是在应用程序中添加了 build.gradle.
调试{
testCoverageEnabled 真
}
然后我运行 ./gradlew connectedChec。一切都运行良好。 Android 工作室默认的 Jacoco 对我来说工作正常。我认为也可以创建一个 jacocoTestReport 任务,然后创建代码 coverage.I 不知道为什么 gradle 和 android 工作室以前没有工作。
经过一些额外的搜索,我偶然发现了这个 project
我必须进行一些修改,以便解决方案可以适用于我的项目类型,但现在可以正确生成测试覆盖率报告。
我已将采用的更改推送到我的 example github repo 以防将来有人遇到类似问题。
我使用 blog post 为 gradle 1.2 设置了我的单元测试。然后我拼凑了在这里和其他地方找到的信息,以将代码覆盖率添加到独立模块而不是整个项目。在我的库模块 build.gradle
文件中,我添加了以下内容:
apply plugin: 'jacoco'
def jacocoExcludes = [
'com/mylibrary/excludedpackage/**'
]
android {
...
}
android.libraryVariants.all { variant ->
task("test${variant.name.capitalize()}WithCoverage", type: JacocoReport, dependsOn: "test${variant.name.capitalize()}") {
group = 'verification'
description = "Run unit test for the ${variant.name} build with Jacoco code coverage reports."
classDirectories = fileTree(
dir: variant.javaCompile.destinationDir,
excludes: rootProject.ext.jacocoExcludes.plus(jacocoExcludes)
)
sourceDirectories = files(variant.javaCompile.source)
executionData = files("${buildDir}/jacoco/test${variant.name.capitalize()}.exec")
reports {
xml.enabled true
xml.destination "${buildDir}/reports/jacoco/${variant.name}/${variant.name}.xml"
html.destination "${buildDir}/reports/jacoco/${variant.name}/html"
}
}
}
并且在我的项目 build.gradle
文件中,我添加了常见的排除项:
ext.jacocoExcludes = [
'android/**',
'**/*$$*',
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Service.*'
]
此外,看起来单元测试的代码覆盖率可能会在未来内置Issue 144664
您可以尝试使用这个 Gradle 插件:
https://github.com/arturdm/jacoco-android-gradle-plugin
基本上,您需要做的就是像这样应用它:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.dicedmelon.gradle:jacoco-android:0.1.1'
}
}
apply plugin: 'com.android.library' // or 'com.android.application'
apply plugin: 'jacoco-android'
因此,您应该为每个变体获得一个 JacocoReport
任务。 运行 下面的命令为它们生成代码覆盖率报告。
$ ./gradlew jacocoTestReport
麻烦之后,我决定为此创建一个 open source Gradle plugin。
根目录build.gradle
buildscript {
repositories {
mavenCentral() // optional if you have this one already
}
dependencies {
classpath 'com.vanniktech:gradle-android-junit-jacoco-plugin:0.16.0'
}
}
apply plugin: 'com.vanniktech.android.junit.jacoco'
然后直接执行
./gradlew jacocoTestReportDebug
它将 运行 在调试模式下进行 JUnit 测试,然后在相应的构建目录中以 XML 和 HTML 形式为您提供 Jacoco 输出。
它也支持风味。将创建红色和蓝色两种口味的任务
- jacocoTestReportRedDebug
- jacocoTestReportBlueDebug
- jacocoTestReportRedRelease
- jacocoTestReportBlueRelease
我最近开始在我的一个项目中集成 android-gradle-plugin
1.1.0。该项目使用 robolectric 2.4
到 运行 单元测试。
这是一个多模块项目,依赖关系非常复杂(一些模块依赖于其他模块)。类似的东西:
--> application-module (dependsOn: module1, module2, module-core)
--> module1 (dependsOn: module-core)
--> module2 (dependsOn: module-core)
--> module-core (dependsOn: module3, module4)
--> module3 (library dependencies)
--> module4 (library dependencies)
更清晰的图片请查看jacoco-example项目。
我试图集成 JaCoCo 来为单元测试生成报告,但在我看来它 运行 只是 androidTests
基本上是仪器测试。
经过一些 google'ing 我在 GitHub 和其他文章中遇到了一些项目,但它们主要集中在 android-gradle-plugin
的早期版本或正在使用其他第三方插件,例如 android-unit-test
for example here.
可能是我失去了 google 的能力。但是有人可以指出我可以找到一些关于 android gradle 插件中的新内容以及如何 运行 仅用于单元测试的 jacoco 任务的文档的方向吗?
更新
采用了 nenick's example 中的脚本:
apply plugin: "jacoco"
configurations {
jacocoReport
}
task jacocoReport(dependsOn: 'testDebug') << {
ant {
taskdef(name:'jacocoreport',
classname: 'org.jacoco.ant.ReportTask',
classpath: configurations.jacocoReport.asPath)
mkdir dir: "${buildDir}/test-coverage-report"
mkdir dir: "${buildDir}/reports/jacoco/test/"
jacocoreport {
executiondata = files("${buildDir}/jacoco/testDebug.exec")
structure(name: "${rootProject.name}") {
classfiles {
fileset (dir: "${buildDir}/intermediates/classes/debug") {
//exclude(name: '**/*_*.class')
exclude(name: '**/R.class')
exclude(name: '**/R$*.class')
exclude(name: '**/BuildConfig.class')
}
}
sourcefiles {
fileset dir: "src/main/java"
fileset dir: "${buildDir}/generated/source/buildConfig/debug"
fileset dir: "${buildDir}/generated/source/r/debug"
}
}
xml destfile: "${buildDir}/reports/jacoco/test/jacocoTestReport.xml"
html destdir: "${buildDir}/test-coverage-report/"
}
}
}
dependencies {
jacocoReport 'org.jacoco:org.jacoco.ant:0.7.2.201409121644'
}
之后 ./gradlew jacocoReport
执行并生成报告,但它显示 0(零)测试覆盖率,这是不可能的,因为至少有一半 类 被测试。
UPDATE_2
尝试了这个 example。将下一个任务添加到我的 gradle 个构建文件中:
task jacocoTestReport(type:JacocoReport, dependsOn: "testDebug") {
group = "Reporting"
description = "Generate Jacoco coverage reports"
classDirectories = fileTree(
dir: "${buildDir}/intermediates/classes/debug",
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/BuildConfig.*',
'**/Manifest*.*']
)
sourceDirectories = files("${buildDir.parent}/src/main/java")
additionalSourceDirs = files([
"${buildDir}/generated/source/buildConfig/debug",
"${buildDir}/generated/source/r/debug"
])
executionData = files("${buildDir}/jacoco/testDebug.exec")
reports {
xml.enabled = true
html.enabled = true
}
}
同样的问题,生成了报告,但代码覆盖率仍然为零。
UPDATE_3
它接缝来自 UPDATE_2 的任务有效但仅适用于具有 apply plugin: 'com.android.application'
的模块(正确生成的报告)。但是对于 android 库 (apply plugin: 'com.android.library'
) 的模块,报告显示覆盖率为零,尽管模块包含的测试比应用程序模块多。
UPDATE_4
创建了一个简单的示例项目来演示我的问题。当前,如果您 运行 ./gradlew jacocoReport
会生成报告,但不会显示模块项目的测试覆盖率。看到这个 link
简短说明:当测试是 AndroidUnitTests(whiteout JUnit 4 和 Robolectric)时,JaCoCo 报告显示了所有模块的覆盖率。
有什么想法吗?
警告:这是黑客攻击!使用上面的配置,我整理了一个 hack,根据选择的构建任务在应用程序和库之间切换 android 插件。这对我来说效果很好,因为我最终不会在设置了应用程序模式的情况下提交代码。
// dynamically change the android plugin to application if we are running unit tests or test reports.
project.ext.androidPlugin = 'com.android.library'
for (String taskName : project.gradle.startParameter.taskNames) {
if (taskName.contains('UnitTest') || taskName.contains('jacocoTestReport')) {
project.ext.androidPlugin = 'com.android.application'
break
}
}
logger.lifecycle("Setting android pluging to ${project.ext.androidPlugin}")
apply plugin: project.ext.androidPlugin
...
apply plugin: 'jacoco'
configurations {
jacocoReport
}
task jacocoTestReport(type:JacocoReport, dependsOn: "testDebug") {
group = "Reporting"
description = "Generate Jacoco coverage reports"
classDirectories = fileTree(
dir: "${buildDir}/intermediates/classes/debug",
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/BuildConfig.*',
'**/Manifest*.*']
)
sourceDirectories = files("${buildDir.parent}/src/main/java")
additionalSourceDirs = files([
"${buildDir}/generated/source/buildConfig/debug",
"${buildDir}/generated/source/r/debug"
])
executionData = files("${buildDir}/jacoco/testDebug.exec")
reports {
xml.enabled = true
html.enabled = true
}
}
希望 android 工具团队尽快修复此问题。
请创建一个例子,我可以看看。我猜这是一些缺少的路径配置。
- 包含所有覆盖率文件 (*.exec)
- 添加所有源路径(module/src/main/java)
- 添加所有 class 路径 (module/build/intermediates/classes/debug)
这里有两个例子它看起来如何
我解决了 JaCoCo 的问题并使其与最新的 gradle android 插件 1.1.3
一起工作具有最新 gradle 脚本的项目:https://github.com/OleksandrKucherenko/meter
References:
How to attach own implementation instead of Mocks in Android Studio Unit Tests? https://plus.google.com/117981280628062796190/posts/8jWV22mnqUB
Small hint for everyone who try to use JaCoCo coverage in android builds... unexpected finding!!! https://plus.google.com/117981280628062796190/posts/RreU44qmeuP
JaCoCo XML/HTML Report for Unit Tests https://plus.google.com/u/0/+OleksandrKucherenko/posts/6vNWkkLed3b
我终于能够使用 Android Studio 1.1 查看 JUnit 测试的代码覆盖率。
jacoco.gradle
apply plugin: 'jacoco'
jacoco {
toolVersion "0.7.1.201405082137"
}
def coverageSourceDirs = [
"$projectDir/src/main/java",
]
task jacocoTestReport(type: JacocoReport, dependsOn: "testDebug") {
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
reports {
xml.enabled = true
html.enabled = true
}
classDirectories = fileTree(
dir: './build/intermediates/classes/debug',
excludes: ['**/R*.class',
'**/*$InjectAdapter.class',
'**/*$ModuleAdapter.class',
'**/*$ViewInjector*.class'
]
)
sourceDirectories = files(coverageSourceDirs)
executionData = files("$buildDir/jacoco/testDebug.exec")
// Bit hacky but fixes https://code.google.com/p/android/issues/detail?id=69174.
// We iterate through the compiled .class tree and rename $$ to $.
doFirst {
new File("$buildDir/intermediates/classes/").eachFileRecurse { file ->
if (file.name.contains('$$')) {
file.renameTo(file.path.replace('$$', '$'))
}
}
}
}
然后在模块的 build.gradle 文件中(我把它放在 android
和 dependencies
之间):
apply from: '../jacoco.gradle'
也在 android
的 defaultConfig
块中。我添加了这个(不知道是否有必要,但我从 this blog 那里得到了这个):
android {
defaultConfig {
testHandleProfiling true
testFunctionalTest true
}
}
尽情享受吧。
我遇到了和你一样的问题。今天我确实完全删除了 android studio,android sdk,gradle。然后重新安装一切。在那之后,我只是在应用程序中添加了 build.gradle.
调试{ testCoverageEnabled 真 } 然后我运行 ./gradlew connectedChec。一切都运行良好。 Android 工作室默认的 Jacoco 对我来说工作正常。我认为也可以创建一个 jacocoTestReport 任务,然后创建代码 coverage.I 不知道为什么 gradle 和 android 工作室以前没有工作。
经过一些额外的搜索,我偶然发现了这个 project 我必须进行一些修改,以便解决方案可以适用于我的项目类型,但现在可以正确生成测试覆盖率报告。
我已将采用的更改推送到我的 example github repo 以防将来有人遇到类似问题。
我使用 blog post 为 gradle 1.2 设置了我的单元测试。然后我拼凑了在这里和其他地方找到的信息,以将代码覆盖率添加到独立模块而不是整个项目。在我的库模块 build.gradle
文件中,我添加了以下内容:
apply plugin: 'jacoco'
def jacocoExcludes = [
'com/mylibrary/excludedpackage/**'
]
android {
...
}
android.libraryVariants.all { variant ->
task("test${variant.name.capitalize()}WithCoverage", type: JacocoReport, dependsOn: "test${variant.name.capitalize()}") {
group = 'verification'
description = "Run unit test for the ${variant.name} build with Jacoco code coverage reports."
classDirectories = fileTree(
dir: variant.javaCompile.destinationDir,
excludes: rootProject.ext.jacocoExcludes.plus(jacocoExcludes)
)
sourceDirectories = files(variant.javaCompile.source)
executionData = files("${buildDir}/jacoco/test${variant.name.capitalize()}.exec")
reports {
xml.enabled true
xml.destination "${buildDir}/reports/jacoco/${variant.name}/${variant.name}.xml"
html.destination "${buildDir}/reports/jacoco/${variant.name}/html"
}
}
}
并且在我的项目 build.gradle
文件中,我添加了常见的排除项:
ext.jacocoExcludes = [
'android/**',
'**/*$$*',
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Service.*'
]
此外,看起来单元测试的代码覆盖率可能会在未来内置Issue 144664
您可以尝试使用这个 Gradle 插件: https://github.com/arturdm/jacoco-android-gradle-plugin
基本上,您需要做的就是像这样应用它:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.dicedmelon.gradle:jacoco-android:0.1.1'
}
}
apply plugin: 'com.android.library' // or 'com.android.application'
apply plugin: 'jacoco-android'
因此,您应该为每个变体获得一个 JacocoReport
任务。 运行 下面的命令为它们生成代码覆盖率报告。
$ ./gradlew jacocoTestReport
麻烦之后,我决定为此创建一个 open source Gradle plugin。
根目录build.gradle
buildscript {
repositories {
mavenCentral() // optional if you have this one already
}
dependencies {
classpath 'com.vanniktech:gradle-android-junit-jacoco-plugin:0.16.0'
}
}
apply plugin: 'com.vanniktech.android.junit.jacoco'
然后直接执行
./gradlew jacocoTestReportDebug
它将 运行 在调试模式下进行 JUnit 测试,然后在相应的构建目录中以 XML 和 HTML 形式为您提供 Jacoco 输出。
它也支持风味。将创建红色和蓝色两种口味的任务
- jacocoTestReportRedDebug
- jacocoTestReportBlueDebug
- jacocoTestReportRedRelease
- jacocoTestReportBlueRelease