Android test code coverage with JaCoCo Gradle plugin
Here is how I'm using Jacoco
:
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
classpath 'org.robolectric:robolectric-gradle-plugin:0.11.+'
}
}
apply plugin: 'com.android.application'
apply plugin: 'robolectric'
apply plugin: 'jacoco'
android {
compileSdkVersion 20
buildToolsVersion "20.0.0"
defaultConfig {
applicationId "YOUR_PACKAGE_NAME"
minSdkVersion 10
targetSdkVersion 20
testHandleProfiling true
testFunctionalTest true
}
buildTypes {
debug {
testCoverageEnabled false
}
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
jacoco {
version "0.7.1.201405082137"
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES.txt'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/notice.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/dependencies.txt'
exclude 'META-INF/LGPL2.1'
exclude 'META-INF/services/javax.annotation.processing.Processor'
exclude 'LICENSE.txt'
}
}
robolectric {
include '**/*Test.class'
exclude '**/espresso/**/*.class'
maxHeapSize "2048m"
}
jacoco {
toolVersion "0.7.1.201405082137"
}
// Define coverage source.
// If you have rs/aidl etc... add them here.
def coverageSourceDirs = [
'src/main/java',
]
task jacocoTestReport(type: JacocoReport, dependsOn: "connectedDebugAndroidTest") {
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
reports {
xml.enabled = true
html.enabled = true
}
classDirectories = fileTree(
dir: './build/intermediates/classes/debug',
excludes: ['**/R*.class',
'**/*$InjectAdapter.class',
'**/*$ModuleAdapter.class',
'**/*$ViewInjector*.class'
])
sourceDirectories = files(coverageSourceDirs)
executionData = files("$buildDir/jacoco/testDebug.exec")
// Bit hacky but fixes https://code.google.com/p/android/issues/detail?id=69174.
// We iterate through the compiled .class tree and rename $$ to $.
doFirst {
new File("$buildDir/intermediates/classes/").eachFileRecurse { file ->
if (file.name.contains('$$')) {
file.renameTo(file.path.replace('$$', '$'))
}
}
}
}
dependencies {
androidTestCompile('junit:junit:4.11') {
exclude module: 'hamcrest-core'
}
androidTestCompile('org.robolectric:robolectric:2.3') {
exclude module: 'classworlds'
exclude module: 'maven-artifact'
exclude module: 'maven-artifact-manager'
exclude module: 'maven-error-diagnostics'
exclude module: 'maven-model'
exclude module: 'maven-plugin-registry'
exclude module: 'maven-profile'
exclude module: 'maven-project'
exclude module: 'maven-settings'
exclude module: 'nekohtml'
exclude module: 'plexus-container-default'
exclude module: 'plexus-interpolation'
exclude module: 'plexus-utils'
exclude module: 'wagon-file'
exclude module: 'wagon-http-lightweight'
exclude module: 'wagon-http-shared'
exclude module: 'wagon-provider-api'
exclude group: 'com.android.support', module: 'support-v4'
}
}
The above code also contains a workaround for https://code.google.com/p/android/issues/detail?id=69174.
More details: http://chrisjenx.com/gradle-robolectric-jacoco-dagger/
android jacoco coverage shows 0% with gradle however there are 95% tests covering code
Generation of coverage for your project using command
./gradlew clean createDebugCoverageReport
works just fine:
The only thing that I changed - is compileSdkVersion
from 23
to 25
and buildToolsVersion
from 23.0.1
to 25.0.2
, because this is the versions that I have.
How do I get a jacoco coverage report using Android gradle plugin 0.10.0 or higher?
Over the hundreds of times searching the answer to getting a coverage report, I finally found an exact answer what I want.
From the this blog post, I found that gradlew createDebugCoverageReport
creates the jacoco coverage report.
Also, from the gradle plugin source code, the plugin uses jacoco 0.6.2.201302030002 by default. (therefore, jacoco version definition is not required if you are going to use a default version)
In summary, the ESSENTIAL steps to get a jacoco
coverage report with Android gradle plugin are:
- Android gradle plugin version 0.10.0 or higher (typically in your project's
build.gradle
) - add
testCoverageEnabled true
to the build type you want (i.e.debug
) - run
$ gradlew createDebugCoverageReport
orgradlew connectedCheck
to get ajacoco
coverage report.
You can find your coverage report at the build/reports/coverage/{buildType}
. (i.e. build/reports/coverage/debug
for debug build)
(Add multi-flavor case from @odiggity's comment)
If your project uses multi-flavor configuration, use create{flavorName}CoverageReport
instead. The coverage report will be generated at build/reports/coverage/{flavorName}/{buildType}
.
Example for flavor krInternal with debug build type:
- Command:
./gradlew createKrInternalDebugCoverageReport
- Report is genarated at:
build/reports/coverage/krInternal/debug
Tip :
Since you can only get a coverage report with the emulator
and device with root permission
, you'll get following error after running a command on the regular(non-rooted) device:
05:48:33 E/Device: Error during Sync: Permission denied
java.io.IOException: com.android.ddmlib.SyncException: Permission denied
at com.android.builder.testing.ConnectedDevice.pullFile(ConnectedDevice.java:114)
at com.android.builder.internal.testing.SimpleTestCallable.call(SimpleTestCallable.java:158)
at com.android.builder.internal.testing.SimpleTestCallable.call(SimpleTestCallable.java:42)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:695)
Caused by: com.android.ddmlib.SyncException: Permission denied
at com.android.ddmlib.SyncService.doPullFile(SyncService.java:511)
at com.android.ddmlib.SyncService.pullFile(SyncService.java:320)
at com.android.ddmlib.Device.pullFile(Device.java:849)
at com.android.builder.testing.ConnectedDevice.pullFile(ConnectedDevice.java:107)
... 10 more
:myDirections:connectedAndroidTest FAILED
FAILURE: Build failed with an exception.
Travis-CI build script to get code coverage
Include this block in build.gradle, for all modules (library, sample, etc)
android {
lintOptions {
abortOnError false
}
}
Below is the .travis-ci.yml
file
language: android
jdk: oraclejdk8
sudo: required
android:
components:
# Uncomment the lines below if you want to
# use the latest revision of Android SDK Tools
- tools
- platform-tools
# The BuildTools version used by your project
- build-tools-28.0.3
# The SDK version used to compile your project
- android-28
- android-22
- add-on
# Additional components
- extra-google-google_play_services
- extra-android-support
- extra-google-m2repository
- extra-android-m2repository
# Specify at least one system image,
# if you need to run emulator(s) during your tests
- sys-img-armeabi-v7a-android-22
licenses:
- 'android-sdk-preview-license-52d11cd2'
- 'android-sdk-license-.+'
- 'google-gdk-license-.+'
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
- $HOME/.android/build-cache
before_install:
- yes | sdkmanager "build-tools;28.0.3"
before_script:
- echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a -c 100M
- emulator -avd test -no-audio -no-window &
- android-wait-for-emulator
- sleep 180
- adb devices
- adb shell input keyevent 82 &
script:
- ./gradlew build connectedCheck
after_success:
- bash <(curl -s https://codecov.io/bash)
How to run JaCoCo in other Android Studio sub modules?
I applied the solution from https://thsaravana.github.io/blog/jacoco-single-coverage-for-multi-module/
In the first step for each module reports were generated and in the second step these reports were merged ino one report.
I was not dependant on other libraries than Jacoco.
Related Topics
Android Sax Parser Not Getting Full Text from Between Tags
Cannot Deserialize Instance of Object Out of Start_Array Token in Spring Webservice
Using an Android Library Project Activity Within Another Project
Adding Multiple Markers in Google Maps API V2 Android
Httpclient on Android:Nohttpresponseexception Through Umts/3G
Screen Overlay Detected Blocks Android Permissions
Sending Pictures to a Web Server
Add an Image from Url into Custom Infowindow Google Maps V2
Android Can't Record Video with Front Facing Camera, Mediarecorder Start Failed: -19
Android App Integrated with Ok Google
How to Apply Plugin to Only One Flavor in Gradle
How to Use Selector to Tint Imageview
Android: Drawing Custom Shapes
Nullpointerexception from Google Maps
Android: How to Enable/Disable an Activity's Intent Filter Programmatically