问题
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"
])}
回答1:
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
回答2:
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
回答3:
Instead of using getByPath
You can also use variables to reach different modules the build.gradle itself e.g $buildDir
will take you to current module build
folder.
Secondly, $project.projectDir.parent
will take to parent project. Example $project.projectDir.parent/<sub-project-name>/outputs/code-coverage/connected/coverage.ec
You can use your sub project name : gcm_demo, googleservices or networkcommunication
as $project.projectDir.parent/gcm_demo/outputs/code-coverage/connected/coverage.ec
Note: make sure you use right file coverage.ec
OR coverage.exec
check whats being generated for you
To print all the paths, you can use the following task in the build.gradle:
run gradle paths
as defined as
task paths {
println "Printing the current module build: $buildDir"
println "Printing the module directory: $project.projectDir"
println "Printing the parent module: $project.projectDir.parent"
}
This will help you play with directories and file folders in the multi-module android projects. Problem for me was not able reach to the right folders and file directories from the sub-module build.gradle
来源:https://stackoverflow.com/questions/37693566/android-test-coverage-report-for-multi-module-app