2

I have a project structue like:

--app

--module2 //library module

--module3 //library module

I am writing instrumentation test cases for my multi module android project with jacoco code coverage. If I execute the instrumentation test cases from 'app' module, code coverage is only generated for 'app' module classes.

So, in order to get code coverage for 'module2' & 'module3' classes; I have written instrumentation test cases in respective modules.

Issue arises when I execute instrumentation test cases in non-app modules, am unable to launch the main activity, app is not launching during instrumentation and test cases are failing.

Root cause is:

@Rule
public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);

@Rule is not declared in 'module2' & 'module3' which launch 'MainActivity.class'. I cannot import 'MainActiviy' to these modules because of some circular dependency issues.

Error:

java.lang.RuntimeException: No activities found. Did you forget to launch the activity by calling getActivity() or startActivitySync or similar?

How to tackle this ?

NB: This question is exclusively for instrumentation test, not for unit test(this is already handled in unit tests)

Similiar question

KH_AJU
  • 109
  • 2
  • 18
  • Something about `MainActivity` is not clear. You mentioned that the test code in `module2` & `module3` have `Rule` to launch `MainActivity` but `MainActivity` is in the `app` module? – ahasbini Sep 24 '20 at 21:59
  • 1
    I'm guessing that `module2` & `module3` are just libraries used in `app` module and they don't have any `Activity` classes in them. You could approach this in one of two ways: 1) in case the test case requires a `Context`, you could use the instrumentation test `Context` rather than your app `Context` to fulfill a dependency for some tests. 2) Implement an `Activity` that can be launched by the instrumentation test case. The code for that should be inside the `androidTest` folder and you need to manage the `AndroidManifest.xml` for `andoidTest` to declare the `Activity`. – ahasbini Sep 24 '20 at 22:10
  • @ahasbini is it possible to launch app `MainActivity` from module2/module3 library modules ? – KH_AJU Sep 25 '20 at 06:33
  • 1
    If `app` depends on `module2/module3` then you can't launch `MainActivity` during instrumentation test since `MainActivity` is not part of `module2/module3` `main` or `androidTest` code. When running an instrumentation test for a module, Android Studio/Gradle compile the `main` and `androidTest` code for that module along with any `main` code only of the submodules it depends on. – ahasbini Sep 25 '20 at 07:55
  • In that case I need to write instrumentation test cases in `app` module itself and follow this [answer](https://stackoverflow.com/questions/58415184/is-it-possible-for-instrumented-tests-to-create-coverage-for-a-library-module?noredirect=1&lq=1). – KH_AJU Sep 25 '20 at 08:57
  • Yes it does make sense to tweak the input files to jacoco to add coverage for the submodules, that way it is no longer needed to (re-)write the test cases for the submodules which will save time. This would be good for the short run, though my advice for the long run is to try "splitting" the jacoco coverage reports and the test cases to have, as much as possible, independent and isolated builds for the submodules without needing to build `app` module. In either way, both of these options are viable. – ahasbini Sep 25 '20 at 16:01

1 Answers1

0

I have followed this anwer and changed the gradle task according to my requirments. Adding the gradle task for reference.

 task jacocoUiTestReportAllModules(type: JacocoReport, dependsOn: 'createDebugCoverageReport') {
    group "Reports"
    description "Generate Jacoco Instrumented Tests coverage reports for all modules"
    reports {
        xml.enabled = true
        html.enabled = true
        html.destination file("${rootProject.buildDir}/coverage-report")
    }
    def javaClasses = []
    def kotlinClasses = []
    def javaSrc = []
    def kotlinSrc = []
    def execution = []
    rootProject.subprojects.each { proj ->
        javaClasses << fileTree(dir: "$proj.buildDir/intermediates/javac/debug", excludes: fileFilter)
        kotlinClasses << fileTree(dir: "$proj.buildDir/tmp/kotlin-classes/debug", excludes: fileFilter)
        javaSrc << "$proj.projectDir/src/main/java"
        kotlinSrc << "$proj.projectDir/src/main/kotlin"
        execution << fileTree(dir: proj.buildDir, includes: [
                'outputs/code_coverage/debugAndroidTest/connected/**/*.ec'])
    }
    getSourceDirectories().setFrom(files([javaSrc, kotlinSrc]))
    getClassDirectories().setFrom(files([javaClasses, kotlinClasses]))
    getExecutionData().setFrom(execution)

    doLast() {
        print "file://${reports.html.destination}/index.html"

    }
}

After running gradlew jacocoUiTestReportAllModules the report with full coverage will be present in [projRoot]/build/coverage-report.

KH_AJU
  • 109
  • 2
  • 18