The testing landscape has changed since my first jacoco post. A bug was introduced in v22 of the tools. If you are interested you can find the details at https://code.google.com/p/android/issues/detail?id=170607. The gist of the bug is the coverage report file is empty which interferes with JaCoCo working. Basically, it shows you 0% coverage across the board.
The good news is that there is a workaround! You have to create a custom test runner and then everything is golden. Since some the last post is invalid now, I am including the full steps to getting JaCoCo to work for the android tests again.
1. Creating the Workaround Test Runner
- Copy the following class into the androidTest folder:
1: 2: /**
3: * An annoying work around for
4: * <a href="https://code.google.com/p/android/issues/detail?id=170607">issue 170607</a>
5: */
6: public class JacocoTestRunner extends AndroidJUnitRunner {
7: 8: static {
9: System.setProperty("jacoco-agent.destfile",
10: "/data/data/"+BuildConfig.APPLICATION_ID+"/coverage.ec");
11: } 12: 13: @Override14: public void finish(int resultCode, Bundle results) {
15: try {
16: Class rt = Class.forName("org.jacoco.agent.rt.RT");
17: Method getAgent = rt.getMethod("getAgent");
18: Method dump = getAgent.getReturnType().getMethod("dump", boolean.class);
19: Object agent = getAgent.invoke(null); 20: dump.invoke(agent, false); 21: }22: catch (Throwable e) {
23: e.printStackTrace(); 24: }25: super.finish(resultCode, results);
26: } 27: }2. Update Gradle
- Make the following updates to the modules gradle file (the real file can be at build_coverage.gradle):
1: ...2: apply plugin: "jacoco"
3: 4: 5: android { 6: ... 7: jacoco {8: version "0.7.1.201405082137"
9: } 10: 11: defaultConfig { 12: ...13: //workaround for issue 170607.
14: testInstrumentationRunner "com.fsk.mynotes.JacocoTestRunner"
15: //testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
16: } 17: 18: buildTypes { 19: debug { 20: testCoverageEnabled = true 21: } 22: } 23: 24: //
25: // dexOptions {
26: // incremental true
27: // }
28: ... 29: } 30: 31: task jacocoTestReport(type: JacocoReport) { 32: 33: def coverageSourceDirs = [34: 'src/main/java'
35: ] 36: 37: group = "Reporting"
38: description = "Generates Jacoco coverage reports"
39: reports { 40: xml { 41: enabled = true42: destination "${buildDir}/reports/jacoco/jacoco.xml"
43: } 44: csv.enabled false 45: html { 46: enabled true47: destination "${buildDir}/jacocoHtml"
48: } 49: } 50: 51: classDirectories = fileTree(52: dir: 'build/intermediates/classes/debug',
53: excludes: ['**/R.class',
54: '**/R$*.class',
55: '**/BuildConfig.*',
56: '**/Manifest*.*',
57: '**/*Activity*.*',
58: '**/*Fragment*.*'
59: ] 60: ) 61: 62: sourceDirectories = files(coverageSourceDirs) 63: additionalSourceDirs = files(coverageSourceDirs)64: executionData = files('build/outputs/code-coverage/connected/coverage.ec')
65: 66: doFirst {67: new File("$buildDir/intermediates/classes/").eachFileRecurse { file ->
68: if (file.name.contains('$$')) {
69: file.renameTo(file.path.replace('$$', '$'))
70: } 71: } 72: } 73: } 74: 75: dependencies { 76: ... 77: 78: //Unit test dependencies
79: testCompile 'junit:junit:4.12'
80: testCompile 'org.mockito:mockito-core:1.10.19'
81: testCompile 'org.powermock:powermock-module-junit4:1.6.2'
82: testCompile 'org.powermock:powermock-api-mockito:1.6.2'
83: 84: androidTestCompile 'org.hamcrest:hamcrest-library:1.1'
85: androidTestCompile 'com.android.support.test:runner:0.3'
86: androidTestCompile 'com.android.support.test:rules:0.3'
87: androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2'
88: androidTestCompile "com.google.dexmaker:dexmaker:1.2"
89: androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
90: }- Sync the project to the gradle file. This should happen automatically, but if it doesn’t go to Tools=>Android=>Sync Project with Gradle Files.
3. Running the tests for Coverage
- Start an emulator or a device that can run the tests.
- Open the Android Studio Terminal and enter the following commands:
1: gradlew :MyNotes:createDebugCoverageReport 2: gradlew :MyNotes:jacocoTestReport(substitute the MyNotes with your module name)
- Assuming that everything succeeded, open the module’s build\jacocoHtml folder.
- Right-Click on the index.html file and open it in a browser.
Caveats
- The androidTests only contain the tests that I either can’t get working as local tests or that will take more work than it is worth. As a result, I have two test categories, Android Tests and Local Tests, that give me coverage for different areas of the code. Unfortunately, this means that I will likely never get a single tool to give me 100% coverage.
- Sometimes the terminal commands will fail or will not produce a coverage report. When that happens just clean the build and repeat the terminal commands.
- I created a special gradle file, build_coverage.gradle, to contain the changes for the coverage. I didn’t want it in my main gradle file because I do not want to deliver the changes in the release version. When I want to run coverage, I copy the special gradle file into the main gradle contents and then revert the change when I am done.
Commits
The majority of the changes can be found on github at https://github.com/fsk-software/mynotes/commit/ed209572c5e1302ac03288a84b4e2003cb5894e7.
No comments :
Post a Comment