Need help on enforcing code coverage metrics. I have based my solution on this blog post. I have already referred following posts in an attempt to solve this problem.
- this series of posts
- this one on Jacoco with java project (not android)
- this official documentation
- this one was supposed to be a straight answer
- many more SO questions and blogs
(Really embarrassing - still it looks like I'm missing something :( )
I am able to get the code coverage but not able to enforce minimum threshold.
Here is how my Jacoco.gradle looks like -
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.8.2"
}
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
}
// 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"
// Create coverage task of form 'testFlavorTypeCoverage' depending on 'testFlavorTypeUnitTest'
task "${testTaskName}Coverage" (type:JacocoReport, dependsOn: "$testTaskName") {
reports {
xml.enabled = true
html.enabled = true
}
group = "Reporting"
description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build."
classDirectories = fileTree(
dir: "${project.buildDir}/intermediates/javac/${buildTypeName}",
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")
}
}
}
}
I have included the above script in app module's build.gradle.
apply plugin: 'com.android.application'
apply from: '../jacoco.gradle'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.example.jacocodemo"
minSdkVersion 27
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
testCoverageEnabled = true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
I have tried many different approaches so far. Looks like the Jacoco documentation is biased towards plain java projects as the code snippets almost always need changes when used in android, but even then no luck.
Here's my attempt to solve this - (just give the Jacoco.gradle here as the main code is in it)
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.8.2"
}
// ****** THIS IS MY ATTEMPT AT IT [START] ******
task jacocoTestCoverageVerification(type: JacocoCoverageVerification) {
violationRules {
rule {
enabled = true
limit {
minimum = 0.9
}
}
}
}
check.dependsOn jacocoTestCoverageVerification
// ****** THIS IS MY ATTEMPT AT IT [END] ******
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
}
// 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"
// Create coverage task of form 'testFlavorTypeCoverage' depending on 'testFlavorTypeUnitTest'
task "${testTaskName}Coverage" (type:JacocoReport, dependsOn: "$testTaskName") {
reports {
xml.enabled = true
html.enabled = true
}
group = "Reporting"
description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build."
classDirectories = fileTree(
dir: "${project.buildDir}/intermediates/javac/${buildTypeName}",
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")
}
}
}
}
and then I tried invoking it as follows: gradle clean build testDebugUnitTestCoverage jacocoTestCoverageVerification. Any help on this is much appreciated.