AndroidプロジェクトでJUnitテストのコードカバレッジレポートを生成したかったので、JaCoCogradleプラグインを追加しました。これはプロジェクトレベルのbuild.gradle
ファイルです。
apply plugin: 'jacoco'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.Android.tools.build:gradle:2.0.0-beta6'
classpath 'com.neenbedankt.gradle.plugins:Android-apt:1.8'
}
}
allprojects {
repositories {
jcenter()
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
subprojects { prj ->
apply plugin: 'jacoco'
jacoco {
toolVersion '0.7.6.201602180812'
}
task jacocoReport(type: JacocoReport, dependsOn: 'testDebugUnitTest') {
group = 'Reporting'
description = 'Generate Jacoco coverage reports after running tests.'
reports {
xml {
enabled = true
destination "${prj.buildDir}/reports/jacoco/jacoco.xml"
}
html {
enabled = true
destination "${prj.buildDir}/reports/jacoco"
}
}
classDirectories = fileTree(
dir: 'build/intermediates/classes/debug',
excludes: [
'**/R*.class',
'**/BuildConfig*',
'**/*$$*'
]
)
sourceDirectories = files('src/main/Java')
executionData = files('build/jacoco/testDebugUnitTest.exec')
doFirst {
files('build/intermediates/classes/debug').getFiles().each { file ->
if (file.name.contains('$$')) {
file.renameTo(file.path.replace('$$', '$'))
}
}
}
}
}
jacoco {
toolVersion '0.7.6.201602180812'
}
task jacocoFullReport(type: JacocoReport, group: 'Coverage reports') {
group = 'Reporting'
description = 'Generates an aggregate report from all subprojects'
//noinspection GrUnresolvedAccess
dependsOn(subprojects.jacocoReport)
additionalSourceDirs = project.files(subprojects.jacocoReport.sourceDirectories)
sourceDirectories = project.files(subprojects.jacocoReport.sourceDirectories)
classDirectories = project.files(subprojects.jacocoReport.classDirectories)
executionData = project.files(subprojects.jacocoReport.executionData)
reports {
xml {
enabled = true
destination "${buildDir}/reports/jacoco/full/jacoco.xml"
}
html {
enabled = true
destination "${buildDir}/reports/jacoco/full"
}
}
doFirst {
//noinspection GroovyAssignabilityCheck
executionData = files(executionData.findAll { it.exists() })
}
}
./gradlew jacocoFullReport
を実行するとうまく機能します。ただし、残念ながら、RobolectricTestRunner
で実行されたテストのカバレッジは報告されていません(テストで明らかに呼び出された命令は、カバーされたものとして報告されません)。 @RunWith
アノテーションなしでテストするか、MockitoJUnitTestRunner
レポートカバレッジで実行すると問題ありません。
この問題を解決するために助けをいただければ幸いです。
更新1:RobolectricGradleTestRunner
を使用する必要があることに気づきました。しかし、それは役に立ちませんでした。
考えられる回避策の既知の問題-- https://github.com/jacoco/jacoco/pull/288
または、jacocoバージョンを0.7.1.201405082137
にダウングレードします
[〜#〜]更新[〜#〜]
回避策はもう必要ありません。 gradleバージョン2.13
およびjacocoバージョン0.7.6.201602180812
を使用する必要があります。
ルートbuild.gradle
を更新します。
buildscript {
dependencies {
classpath 'org.jacoco:org.jacoco.core:0.7.6.201602180812'
}
}
task wrapper( type: Wrapper ) {
gradleVersion = '2.13'
}
./gradlew wrapper
を実行します
プロジェクトの更新build.gradle
:
apply plugin: 'jacoco'
Android {
testOptions {
unitTests.all {
jacoco {
includeNoLocationClasses = true
}
}
}
}
受け入れられた答えは少し時代遅れです。これは、先ほど実装した同様の修正です。モジュール(つまりアプリ)にbuild.gradleを追加します:
apply plugin: 'jacoco'
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}
これにはJaCoCo7.6以降が必要ですが、すでに使用している可能性があります。
スタジオに関する注意事項:
私は同じ問題に直面していましたが、このリンクをたどることで解決されました。
問題のリンク: https://github.com/robolectric/robolectric/issues/22
この問題の解決策はここに記載されています:
https://github.com/dampcake/Robolectric-JaCoCo-Sample/commit/f9884b96ba5e456cddb3d4d2df277065bb26f1d
私も同じ問題を抱えていました。 jacocoプラグインのバージョンを変更し、includenolocationclassesプロパティを追加しました。動作するjacoco.gradleファイルは次のとおりです(私はgradleラッパー2.14.1を使用しています):
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.7.6.201602180812"
}
Android {
testOptions {
unitTests.all {
jacoco {
includeNoLocationClasses = true
}
}
}
}
project.afterEvaluate {
// Grab all build types and product flavors
def buildTypes = Android.buildTypes.collect { type -> type.name }
def productFlavors = Android.productFlavors.collect { flavor -> flavor.name }
println(buildTypes)
println(productFlavors)
// When no product flavors defined, use empty
if (!productFlavors) productFlavors.add('')
productFlavors.each { productFlavorName ->
buildTypes.each { buildTypeName ->
def sourceName, sourcePath
if (!productFlavorName) {
sourceName = sourcePath = "${buildTypeName}"
} else {
sourceName = "${productFlavorName}${buildTypeName.capitalize()}"
sourcePath = "${productFlavorName}/${buildTypeName}"
}
def testTaskName = "test${sourceName.capitalize()}UnitTest"
println("SourceName:${sourceName}")
println("SourcePath:${sourcePath}")
println("testTaskName:${testTaskName}")
// Create coverage task of form 'testFlavorTypeCoverage' depending on 'testFlavorTypeUnitTest'
task "${testTaskName}Coverage" (type:JacocoReport, dependsOn: "$testTaskName") {
group = "Reporting"
description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build."
classDirectories = fileTree(
dir: "${project.buildDir}/intermediates/classes/${sourcePath}",
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
'**/BuildConfig.*',
'**/Manifest*.*']
)
def coverageSourceDirs = [
"src/main/Java",
"src/$productFlavorName/Java",
"src/$buildTypeName/Java"
]
additionalSourceDirs = files(coverageSourceDirs)
sourceDirectories = files(coverageSourceDirs)
executionData = files("${project.buildDir}/jacoco/${testTaskName}.exec")
println("${project.buildDir}/jacoco/${testTaskName}.exec")
reports {
xml.enabled = true
html.enabled = true
}
}
}
}
}