Setting up an unified coverage report in Android with Jacoco, Robolectric, and Espresso
Hello! In this post we’ll see how you can generate a test coverage report on an Android project including both unit tests (usually written with JUnit, Mockito, and Robolectric) and instrumented (usually written using Espresso).
Test coverage reports are an important tool to measure how much our tests actually exercise our code. Although not guaranteed a bug-free software, to have a high percentage of coverage can avoid a lot of headaches in the project.
To generate the coverage report in Android, we use Jacoco (Java Code Coverage), one of the most used tools in Java for this purpose. But the Android development environment have a particular scenario, as we have two different test artifacts, usually represented by the test folders (unit) and androidTest (instrumented).
First, let’s generate the coverage report of Espresso tests. For this example, we have a simple Activity:
Its layout is:
So, let’s create a test using Espresso to ensure that the text of the TextView is changed to Hello World! when click the button:
After its execution, the execution report will be generated:
However, the coverage report is not generated yet. To enable this option, we need to add a property to our debug build variant. Using the Android plugin DSL, we can enable the coverage through the testCoverageEnabled property:
Now, just run the task createDebugCoverageReport to run the tests and generate the report.
Perfect! We already have our coverage report.
Now, let’s write a test using Robolectric to test the else logic of our Activity. Side note: IMHO, I do not recommend testing the Activity and Android components using Robolectric. Use it primarily for unit tests.
So, a test with Robolectric testing the hide button behavior, whose visibility changes when clicked, would look like this:
By default, the Android plugin only generates the coverage report from instrumented tests. To be able to generate the coverage of unit testing, we must create a task manually:
EDIT: To enable the coverage report for local tests when using version 2.2.+ of Android Gradle plugin, you need to enable it in your app’s build.gradle:
With the task jacocoTestReport created, now we can generate the coverage report also for unit testing.
However, the problem remains: how to merge the coverage results of the two test groups?
The instrumentation performed by Jacoco produces execution files that contain the necessary data to create the report (HTML, XML, etc.). The problem here, is that Espresso generates .ec file, while the unit tests execution generates .exec file… we have different formats!
So, now the question is: how to convert from one format to another? The simple answer is: there’s no need to convert anything!
As we are not able to configure the coverage task of Espresso, we must ensure that it is executed first. Next, we need to run unit tests, and then create the coverage data with both files (ec and exec).
To enable this, we need to edit our task once more and add the coverage.ec file as a parameter in executionData property:
EDIT: As Android Gradle plugin 2.2.+ now generates a coverage file for each execution, using the device / emulator in the file name, now we need to pass every file to execution data, as the file name is now dynamic. In addition, I added the createDebugCoverageReport task as a dependency of our custom task, so we don’t need to run it manually :)
Finally, when we execute both tasks, running the Espresso tests first, we will get the unified coverage report!
gradle clean jacocoTestReport
An example project with all this setup can be found on Github! (EDIT: Updated with latest build tools!) #KeepCoding #KeepTesting
UPDATE: This post is a bit outdated. To make it work with Android Gradle plugin 3.x, check my new post about it! :)