We have a multi module application. Where we have 3 library projects and 1 launch project.
module1 (Libraray) module2 (Libraray) depends on module1 module3 (Libraray)depends on module1
Launch (Does not have any source code its just a launcher for all lib)depends on module1 and module 2.
In module1 we are accessing module 2 and module 3 classes using facade pattern. Due to that we need to write all the test cases in Launch project as we have access the to all the classes in launch project so that we have access to all the classes and test cases will not fail due to NoClassDefException.
When we write the test cases in Launch project then we are able to run the test cases and we are getting the execution report as 100% and it create a index.html file with all the details of test cases but when i try to generate the coverage report then it not showing any data for coverage report. Below is my gradle file.
apply plugin: 'com.android.application'
apply plugin: 'jacoco'
android {
compileSdkVersion 22
buildToolsVersion "23.0.2"`
defaultConfig {
applicationId "com.test.mobile"
minSdkVersion 14
targetSdkVersion 17
multiDexEnabled true
testApplicationId "com.test.mobile.test"
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}
repositories {
mavenCentral()
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
}
debug{
testCoverageEnabled true
}
}
dexOptions {
preDexLibraries = false
javaMaxHeapSize "4096M"
jumboMode = true
incremental false
}
afterEvaluate {
tasks.matching {
it.name.startsWith('dex')
}.each { dx ->
if (dx.additionalParameters == null) {
dx.additionalParameters = []
}
dx.additionalParameters += '--multi-dex'
dx.additionalParameters += "--main-dex-list=$projectDir\\multidex-main-dex-list.txt".toString()
}
}}
dependencies {
compile project(':module2')
compile project(':module3')
compile "com.android.support.test.espresso:espresso-idling-resource:2.2.1"
// Dependencies for local unit tests
testCompile "junit:junit:4.12" exclude group: 'com.android.support', module: 'support-annotations'
testCompile "org.mockito:mockito-all:1.10.19" exclude group: 'com.android.support', module: 'support-annotations'
testCompile "org.hamcrest:hamcrest-all:1.3" exclude group: 'com.android.support', module: 'support-annotations'
testCompile "org.powermock:powermock-module-junit4:1.6.2" exclude group: 'com.android.support', module: 'support-annotations'
testCompile "org.powermock:powermock-api-mockito:1.6.2" exclude group: 'com.android.support', module: 'support-annotations'
// Android Testing Support Library's runner and rules
androidTestCompile "com.android.support.test:runner:0.4.1" exclude group: 'com.android.support', module: 'support-annotations'
androidTestCompile "com.android.support.test:rules:0.4.1" exclude group: 'com.android.support', module: 'support-annotations'
// Espresso UI Testing dependencies.
androidTestCompile "com.android.support.test.espresso:espresso-core:2.2.1" exclude group: 'com.google.code.findbugs' exclude group: 'javax.annotation' exclude group: 'com.android.support', module: 'support-annotations' exclude module: 'javax.annotation-api'
androidTestCompile "com.android.support.test.espresso:espresso-contrib:2.2.1" exclude group: 'com.google.code.findbugs' exclude group: 'javax.annotation' exclude group: 'com.android.support', module: 'support-annotations' exclude module: 'javax.annotation-api' exclude group: 'com.android.support', module: 'support-v4'
androidTestCompile "com.android.support.test.espresso:espresso-intents:2.2.1" exclude group: 'com.google.code.findbugs' exclude group: 'javax.annotation' exclude group: 'com.android.support', module: 'support-annotations' exclude module: 'javax.annotation-api'}
task jacocoTestReport(type: JacocoReport, dependsOn: 'testDebugUnitTest') {
def projects = new ArrayList()
subprojects.each { prj ->
projects.add(prj)
}
reports {
xml.enabled = true
html.enabled = true
}
jacocoClasspath = configurations['androidJacocoAnt']
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"
sourceDirectories = files([mainSrc])
classDirectories = files([debugTree])
/*sourceDirectories = generateSourceFiles(projects)
classDirectories = generateClassDirs(projects)*/
executionData = files(["${buildDir}/jacoco/testDebugUnitTest.exec",
"${buildDir}/outputs/code-coverage/connected/coverage.ec"
])}
I have 3 modules named with gcm_demo, googleservices and networkcommunication so under build.gradle of each module write
apply plugin: 'jacoco'
task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) {
reports {
xml.enabled = true
html.enabled = true
}
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"
sourceDirectories = files([mainSrc])
classDirectories = files([debugTree])
executionData = fileTree(dir: "$buildDir", includes: [
"jacoco/testDebugUnitTest.exec", "outputs/code-coverage/connected/*coverage.ec"
])
}
Now in Project build.gradle write the following scrpit
apply plugin: 'jacoco'
task jacocoRootReport(type: JacocoReport, dependsOn: ['gcm_demo:jacocoTestReport', 'googleservice:jacocoTestReport', 'networkcommunication:jacocoTestReport']) {
reports {
xml.enabled = true
html.enabled = true
}
sourceDirectories = files([tasks.getByPath("gcm_demo:jacocoTestReport").sourceDirectories,
tasks.getByPath("googleservice:jacocoTestReport").sourceDirectories,
tasks.getByPath("networkcommunication:jacocoTestReport").sourceDirectories])
classDirectories = files([tasks.getByPath("gcm_demo:jacocoTestReport").classDirectories,
tasks.getByPath("googleservice:jacocoTestReport").classDirectories,
tasks.getByPath("networkcommunication:jacocoTestReport").classDirectories])
executionData = files([tasks.getByPath("gcm_demo:jacocoTestReport").executionData,
tasks.getByPath("googleservice:jacocoTestReport").executionData,
tasks.getByPath("networkcommunication:jacocoTestReport").executionData])
}
for execution use
gradlew clean jRR (short abbreviation)
after build successful output folder is
{project location}\build\reports\jacoco\jacocoRootReport\html\index.html
it provides the full project coverage of UI and unitTest
This is what we had in our top build.gradle
for generating HTML coverage reports:
def coverageSourceDirs = ['app/src/main/java', 'core/src/main/java', 'database/src/main/java']
def coverageExcludes = ['**/R.class',
'**/R$*.class',
'**/*$$ViewBinder*.*',
'**/inject/*',
'**/*$InjectAdapter.*',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/Dagger*.*',
'**/*_Provide*Factory.*',
'**/*_Member*Injector.*',
'**/*_Factory.*']
def coverageClassDirectories = [fileTree(dir: 'app/build/intermediates/classes/debug', excludes: coverageExcludes),
fileTree(dir: 'core/build/intermediates/classes/debug', excludes: coverageExcludes),
fileTree(dir: 'database/build/intermediates/classes/debug', excludes: coverageExcludes)]
task jacocoRootReport(type: JacocoReport) {
dependsOn "app:jacocoTestReport",
"core:jacocoTestReport",
"database:jacocoTestReport"
additionalSourceDirs = files(coverageSourceDirs)
sourceDirectories = files(coverageSourceDirs)
classDirectories = files(coverageClassDirectories)
executionData = files(tasks.getByPath("app:jacocoTestReport").executionData,
tasks.getByPath("core:jacocoTestReport").executionData,
tasks.getByPath("database:jacocoTestReport").executionData
)
reports {
html.enabled = true
xml.enabled = false
csv.enabled = false
}
onlyIf = {
true
}
doFirst {
executionData = files(executionData.findAll {
it.exists()
})
}
}
And in every submodule:
android {
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
testCoverageEnabled = true
}
}
}
def coverageSourceDirs = ['src/main/java']
task jacocoTestReport(type: JacocoReport, dependsOn: "testJenkinsUnitTest") {
group = "Reporting"
description = "Generate Jacoco coverage reports"
classDirectories = fileTree(dir: 'build/intermediates/classes/debug',
excludes: ['**/R.class',
'**/R$*.class',
'**/*$$ViewBinder*.*',
'**/inject/*',
'**/*$InjectAdapter.*',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/Dagger*.*',
'**/*_Provide*Factory.*',
'**/*_Member*Injector.*',
'**/*_Factory.*',
'**/PagerTitleStripV22*.*'])
additionalSourceDirs = files(coverageSourceDirs)
sourceDirectories = files(coverageSourceDirs)
executionData = files('build/jacoco/testDebugUnitTest.exec')
doFirst {
new File("$buildDir/intermediates/classes/").eachFileRecurse { file ->
if (file.name.contains('$$')) {
file.renameTo(file.path.replace('$$', '$'))
}
}
}
reports {
xml.enabled = false
html.enabled = true
}
}
Note that you don't need it if you do coverage analyse with Jenkins plugin or Sonar.
P.S. if you have any problems please check all paths manually, I might mistype something. It is really a pain to setup it
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With