43

I'm trying to get a test coverage report using Gradle Android plugin 0.10.2. But I still can't get a coverage report after running some tests. (connectedAndroidTest).

my main module's build.gradle is :

apply plugin: 'android'

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.3"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        debug {
            testCoverageEnabled true
        }

        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
    }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:19.+'
}

and the buildscript section of project's build gradle is :

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.10.+'
    }
}

Once I run a gradlew connectedAndroidTest from terminal, I can find coverage-instrumented-classes and code-coverage folder inside the build folder. But I can't find coverage folder in the reports folder. (Only I can see is androidTests folder)

Is there anything missing for getting a jacoco coverage report?

Taeho Kim
  • 1,865
  • 2
  • 14
  • 16

5 Answers5

111

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:

  1. Android gradle plugin version 0.10.0 or higher (typically in your project's build.gradle)
  2. add testCoverageEnabled true to the build type you want (i.e. debug)
  3. run $ gradlew createDebugCoverageReport or gradlew connectedCheck to get a jacoco 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)
Akarsh SEGGEMU
  • 2,011
  • 1
  • 15
  • 18
Taeho Kim
  • 1,865
  • 2
  • 14
  • 16
  • 2
    I'm getting "Task 'createDebugCoverageReport' not found in root project 'MyProject'." whenever I run ./gradlew createDebugCoverageReport. I've made sure to do your first two steps. Any idea why? – odiggity Jul 31 '14 at 14:00
  • 1
    @odiggity Did you Sync? I had the same issue, but after sync and rebuild it showed up and it is fine now. – dragi Sep 26 '14 at 13:55
  • 4
    Came back to this a few months later and figured out why it wasn't working. Seems obvious now but I didn't really understand the gradle command structure back then. For anyone else that is using flavours you need to include those as well so it looks like ./gradlew create{flavourName}DebugCreateCoverageReport. – odiggity Dec 02 '14 at 15:21
  • 1
    @odiggity should be create{flavourName}DebugCoverageReport, right? seems to be typo :) – Better Shao Dec 18 '14 at 11:03
  • @Taeho Kim When I try to run `./gradlew createDebugCoverageReport` I get the following errors: `error: package org.robolectric.annotation does not exist` and `error: package org.junit does not exist`. How can I add those 2 dependencies to `org.robolectric:robolectric:2.4` and `junit:junit:4.+`? I've added them under `dependencies { testCompile '..' }` but that seems to be working only for the acutal tests. – Niklas Jan 08 '15 at 11:54
  • @BetterShao ah good catch my bad. Looks like Taeho updated it in his answer already – odiggity Jan 09 '15 at 15:09
  • @Niklas try change `testCompile` into `androidTestCompile`. There were some changes since plugin version 0.9. – Taeho Kim Jan 09 '15 at 17:37
  • Well I have set `sourceSets { androidTest.setRoot('src/test') }` in my gradle file. Do I need to change that back to `androidTest` then? – Niklas Jan 09 '15 at 18:09
  • 1
    @Niklas Yes. And if you want to add multiple test src dirs, you can try to do it like `androidTest.java.srcDirs = ['src/androidTest/java', 'src/customizedDirName/java']` though typically it's best practice to name your test src dir as `androidTest`, `androidTest{flavorName}` to be consistent to system defaults. – Better Shao Jan 12 '15 at 01:45
  • 1
    Does this only generate a coverage.ec file? What do I do with this file after it's generated? It's just binary data for me – Rhys Davis Feb 01 '15 at 23:10
  • @TaehoKim apparently `gradlew createDebugCoverageReport` requires a connected device which I don't need, is there a way to bypass this? I only need the jacoco results, thanks, Bob – Bob May 20 '15 at 17:41
  • Using `createDebugCoverage` task have you tried excluding multiple classes from coverage reports? – Toochka Aug 26 '15 at 12:07
  • 1
    Is it possible to exclude so source files? – Asker Jan 25 '16 at 01:17
  • 7
    I cannot find any coverage results from my JUnit tests located in src/test. Has anyone managed to get coverage from the "normal" junit-tests? – nilsmagnus Feb 11 '16 at 10:54
  • 1
    @nilsmagnus For unit tests, you should add some jacoco task to get a coverage report. You can find the sample project in here: https://github.com/kunny/blog_samples/tree/master/Android/2016-02-13_JacocoUnitTest – Taeho Kim Feb 14 '16 at 08:13
  • @Bob No, it requires connected devices since it requires an 'instrumentation test' result. If you want to get a coverage report without a device, you can move your test to under `src/test` and create a gradle task for a coverage report. See https://github.com/kunny/blog_samples/tree/master/Android/2016-02-13_JacocoUnitTest for how to get a report from a Unit(not an instrumentation) test. – Taeho Kim Feb 14 '16 at 08:20
  • @TaehoKim, that doesn't seem to do what you are suggesting. It does not use the unit tests (test dirs instead of androidTest dirs). – behelit Mar 17 '16 at 02:37
  • @behelit I can't understand your point. TL;DR: for androidTest(connected test), use the method mentioned in answer, or for test(Unit test) use the method mentioned my comment above. – Taeho Kim Mar 18 '16 at 23:51
  • I need to use `jacocoTestCoverageVerification`, but the usual code in whole of the Internet not working and gradle doesn't know that. can you help me? @TaehoKim – MHSaffari Oct 02 '19 at 05:44
  • There is a way to generate a report that adds reports from unitTest & androidTest?, when I enable "testCoverageEnabled true" "/jacoco/*UnitTest.exec" is not being generated, AGP 7.1.0-alpha05, kotlin v1.5.10. – Akhha8 Sep 22 '21 at 16:44
11

Gradle already has built-in support for generating test coverage reports and we don't need to create any additional configurations or add any plugins to generate test coverage report. Basically, the only thing we need to do, is to set testCoverageEnabled parameter to true in build.gradle file as follows:

android {
   buildTypes {
      debug {
         testCoverageEnabled = true
      }
   }
}

Next, we can execute the following Gradle task from CLI:

./gradlew createDebugCoverageReport

On Windows, we can execute it like this:

gradlew.bat createDebugCoverageReport

Task will analyze code of our project in /src/main/java/ directory and unit tests placed in /src/androidTest/java/ directory. After executing this task, we can find test coverage report in the following directory of the module:

/build/outputs/reports/coverage/debug/

When we open index.html file, we can see visual report from test coverage, which can be viewed in a web browser.

It looks as on the image below.

enter image description here

Yuliia Ashomok
  • 8,336
  • 2
  • 60
  • 69
  • 1
    have you generate a report that includes unit Test and Android Test (instrumented tests)? – Akhha8 Sep 22 '21 at 16:46
  • @Akhha8 have a look at: https://github.com/ChristopherBull/demo-merge-android-coverage (verify the paths you need) – tzanke Jun 07 '22 at 09:08
  • Yuliia, do you have a clue how to get the coverage report even with failing tests? Currently your approach generates the report only if the tests don't fail. – tzanke Jun 07 '22 at 09:09
8

I created an open source plugin for that.

Root build.gradle

apply plugin: "com.vanniktech.android.junit.jacoco"

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.vanniktech:gradle-android-junit-jacoco-plugin:0.3.0'
    }
}

Then simply execute

./gradlew jacocoTestReportDebug // or jacocoTestReportRelease

It'll run the JUnit tests and then give you the Jacoco output in xml and html form in the corresponding build directory for debug build type.

Niklas
  • 23,674
  • 33
  • 131
  • 170
6

Have to add an answer instead of a comment since my reputation is lower than 50...

What I want to supplement is:

Google released new build tools fixing the nasty "VerifyError" (link) issue.

Please try to change your setting in gradle to use latest build tools if you encounter the "VerifyError" issue. For instance,

android {
    ......
    buildToolsVersion '21.1.1'
    .....
}

Since the 21.0.0 build tools is buggy, please use a version greater than 21.0.0. I use 21.1.1.

Better Shao
  • 455
  • 4
  • 10
3

If you want to use a different version than the default, then add

jacoco {
    version = '0.7.3.201502191951'
}

inside the android tag in your app's build.gradle.

One way to find the latest version number: search for "org.jacoco:jacoco" on JCenter.

G. Lombard
  • 3,569
  • 1
  • 29
  • 30
nhaarman
  • 98,571
  • 55
  • 246
  • 278
  • 2
    The default version in Android Gradle Plugin 1.0.0 is now 0.7.1.201405082137. – Better Shao Dec 18 '14 at 11:00
  • @BetterShao I am a little bit struggling, if I run `gradlew createDebugCoverageReport` I don't get any reports. What's the trick? – Niklas Jan 14 '15 at 20:50
  • @Niklas Follow the accepted answer should be enough. One question: are you using Robolectric or the default Android Testing Framework? I noticed that you are trying some dependency of Robolectric. This question should be about Android Testing Framework only. – Better Shao Jan 15 '15 at 03:23
  • 2
    @Niklas createDebugCoverageReport is the default task to create coverage report for android test (instrument test), which takes use of Jacoco to generate coverage report. If you are using Robolectric, IMHO you need to write your own task to execute the test and generate test/coverage reports. – Better Shao Jan 16 '15 at 07:29
  • @Niklas createDebugCoverageReport task will appear only if the build.grade has the following declaration: buildTypes{ debug{ testCoverageEnabled true } } – Rames Palanisamy Jun 10 '15 at 17:36