Code Coverage for Android using Jacoco



Code Coverage for Android using Jacoco for Functional Tests (Manual/Automation Tests)

What is Code Coverage?
Code Coverage is a method of finding out the measure of the developed source code while performing unit and functional testing.

What is Jacoco?
Jacoco is a very effective and promising open-source Code Coverage library developed by EclEmma team for Java and Android projects.

What is Android Studio?
                Android Studio is an official IDE for Google’s Android Operating systems, specifically for Android development.

What is Gradle?
                Gradle is an Android build tool, which allows user to perform all desired tasks smoothly with ease on the Android source code using a Groovy-based domain-specific language (DSL).

How to achieve Code Coverage on Android?
                Jacoco can be used to perform Code Coverage on Android using Android Studio.
                Code Coverage can be easily achieved for unit and functional tests for either of the testing ways mentioned below:
a.       Manual Testing
b.      Automation Testing  (irrespective of Automation languages, supporting usage of languages such as C# and many others)

Types of Instrumentation in Code Coverage?
1.       Source code instrumentation
– adding instructions to source code before compilation.

2.       On the Fly Instrumentation
– adding instructions to source code in the byte code at runtime, when the byte-code is loaded by the JVM. (We are going to use this below)

3.       Offline Instrumentation
– adding instructions to source code after compilation, directly into the byte-code

Type of Issues that one may encounter while working on Code Coverage in Android:
  1. Gradle issues
  2. Jacoco issues
  3. Emulator issues
  4. Android SDK errors
  5. Capturing Code Coverage data in Android device
  6. Generating report in HTML from generated ‘coverage.exec’ file.
Steps to follow to conduct Code Coverage in Android?
  • Install Android Studio and import an working Android project
  • Configure & Build APK
  • Start Code Coverage (Install Instrumented ‘app-debug.apk’ file in Android device)
  • Run test cases manually or using automation
  • Stop Code Coverage (Close the Android app in Android device)
  • Collect Coverage data from device and copying in local system path
  • Generating Report in HTML

Steps to configure Android project with Jacoco for code coverage:

Step 1: Go to ‘app/src/main/AndroidManifest.xml’ path in the imported Android project.
Provide read and write permission in your project to access android storage memory of the Android device for code coverage data generation, tracing and collection of the same. This can be achieved by adding the below lines in ‘AndroidManifest.xml’ file.

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<
uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<
uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<
uses-permission android:name="android.permission.INTERNET"/>

The above lines are to be mentioned in the existing ‘AndroidManifest.xml’ file which looks like below after appending the above permissions:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.writer.aspiring.unittestingdemo">
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Step 2: Add a new folder and let us name it as ‘resources’ in ‘app/src/main’ path of the Android project. Let us add a file and name it as ‘jacoco-agent.properties’ in the same ‘app/src/main/resources’ path

The below line of code is to be added in the newly created ‘jacoco-agent.properties’ file:
        destfile=/storage/sdcard/coverage.exec

Step 3:
 Go to ‘app/build.gradle’ path in the project. (Do not change the other gradle file in project level)

  • Jacoco plugin is to be added by using: ‘apply plugin: 'jacoco'.
  • Few lines of code are to be added in the existing ‘android’ block of ‘build.gradle’ Gradle file to support jacoco.
  •    Block of ‘jacocoTestReport’ task is to be added in the same Gradle file for correct configuration of Jacoco reporting.

The below lines are to be present in the same Gradle file which looks like below after appending the above mentioned items (including the other existing lines of code in build.gradle):

apply plugin: 'com.android.application'
apply plugin: 'jacoco'
android {
    jacoco{
        version "0.7.9"
    }
    sourceSets {
        main {
            resources.includes = ['**/jacoco-agent.properties']
        }
    }
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.writer.aspiring.unittestingdemo"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            testCoverageEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            testCoverageEnabled true
        }
    }
}

task jacocoTestReport(type:JacocoReport) {
    group = "Reporting"
    description = "Generate Jacoco coverage reports"
    // exclude auto-generated classes and tests
    def fileFilter = ['**/R.class', '**/R$*.class',
                      '**/BuildConfig.*', '**/Manifest*.*',
                      'android/**/*.*']
    def debugTree = fileTree(dir:
            "${project.buildDir}/intermediates/classes/debug",
            excludes: fileFilter)
    def mainSrc = "${project.projectDir}/src/main/java"
    sourceDirectories = files([mainSrc])
    classDirectories = files([debugTree])
    additionalSourceDirs = files([
            "${buildDir}/generated/source/buildConfig/debug",
            "${buildDir}/generated/source/r/debug"
    ])
    executionData = fileTree(dir: project.projectDir, includes:
            ['**/*.exec', '**/*.ec'])
    reports {
        xml.enabled = true
        xml.destination = "${buildDir}/jacocoTestReport.xml"
        csv.enabled = false
        html.enabled = true
        html.destination = "${buildDir}/reports/jacoco"
    }
}

dependencies {

    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:25.1.1'
    //noinspection GradleCompatible
    compile 'com.android.support:design:25.1.1'
    testCompile 'junit:junit:4.12'
    testCompile 'org.mockito:mockito-core:1.10.19'
    androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2') {
        exclude module: 'support-annotations'
}
    androidTestCompile('com.android.support.test:runner:0.5') {
        exclude module: 'support-annotations'
    }
    androidTestCompile 'org.mockito:mockito-core:1.10.19'
    androidTestCompile "com.google.dexmaker:dexmaker:1.2"
    androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
    compile 'org.mockito:mockito-core:1.10.19'
    compile 'junit:junit:4.12'
    compile 'junit:junit:4.12'
}

Step 4: Go to ‘MainActivity.java’ class in ‘app/src/main/java/…’ path and add the below lines of code in the same class as mentioned below without changing any word in it:

protected void onStop()
{
Log.d("StorageSt", Environment.getExternalStorageState());
String coverageFilePath = Environment.getExternalStorageDirectory() + File.separator+ "coverage.exec";
File coverageFile = new File(coverageFilePath);
super.onStop();
if(BuildConfig.DEBUG)
{
try{
    Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
    Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData",coverageFile.getClass(),boolean.class,boolean.class);
    dumpCoverageMethod.invoke(null, coverageFile,true,false);
    }
    catch (Exception e) {}
}}

Step 5: Once all the above lines of code are added to respective files, go to ‘Build’ menu bar in Android Studio and click on ‘Build APK’ option in the menu bar.
This action compiles the entire code and generates an output in ‘app/build/outputs/apk/app-debug.apk’ path, if the compiled build is successful.

Step 6: Drag and drop the generated ‘app-debug.apk’ from the project to an already open emulator in the local system. This installs the app in the emulator/device.

Step 7: Post installation of the app in Android device/emulator, perform testing manually in the app OR run functional tests in automation suites.

Step 8: Once all the testing is completed, close the app in the mobile device/emulator.

Step 9: Once the app is closed in the device, ‘coverage.exec’ file with coverage data is to be generated in the “/storage/sdcard/coverage.exec” path of the connected mobile device/emulator.

Step 10: We need to pull this coverage data from device to system to analyze & generate further reports. To do this, let us go back to Android Studio à ‘Terminal’ window.
In ‘Terminal’ Window of Android Studio, type the below command and press Enter key in Keyboard:
adb pull /sdcard/coverage.exec app/build/outputs/coverage.exec

Where,
‘adb pull’ denotes  the default request to add a file from android device,
‘/sdcard/coverage.exec’ denotes the path where coverage file is generated in device, and
‘app/build/outputs/cov.exec’ denotes the path where the coverage file needs to be copied in the project workspace.



Step 11: Once the above mentioned pull is successful, a coverage file with data is to be newly created in ‘app/build/outputs/cov.exec’ project path. Our next goal is to read the exec file and generate results in a legible HTML format for good readability. For this, we can run the already added task - ‘jacocoTestReport’ in gradle file (added in Step 3).

Generation of report can be done in any of the 2 possible ways mentioned below:
            ·  Type the below command in Terminal window of Android Studio & click on Enter:  
                 gradle jacocoTestReport
      OR
·  Go to Gradle pane in Android Studio, click on the Gradle icon (for which the tool tip message notes ‘Execute Gradle Task’)



In the displayed “Run Gradle Task” pop-up box, type the below command in command-line text box and click on 'Ok' button:
jacocoTestReport      

Step 12: On successful generation of jacocoTestReport, a new folder should be auto-generated in the project path – “app/build/reports”

Look for index.html file in the generated reports folder in the same path – “app/build/reports/jacoco/index.html”

Right click on the ‘index.html’ file, select “open in Browser” option and select ‘Chrome’ in the browser list.

A beautiful coverage report with required data should be displayed as below. Click on each sub-files to see detailed reports up to code level as below:

a.       Main Folder:




b. Sub Folders (on click of the main folder)

c. On Click of classes in source code

 d. On click of the methods in classes (to further break down and look into the code on the coverage)



  

2 comments:

Copyright © 2017 QALEARNINGGUIDE.COM || ALL RIGHTS RESERVED