Repository: mockito/mockito
Issue: Fix coverage for subprojects
Labels: enhancement
kotlin
please contribute

In #1032, the new coverage was incorrectly tracked by jacoco. A quick search showed this TODO (https://github.com/mockito/mockito/blob/release/2.x/gradle/root/coverage.gradle#L3) that points out it is actually not generating coverage for subprojects. Would like to see this fixed!

Issue comments:

Pull request review comments:
Maybe it would be better to iterate over subprojects and depend on "test" task (by a type or by a name) and use that list to generate also files to include (istead of looking for `*.exec` file)?

It would be even less fragile to have an optional exclusions for specific modules if needed.
Do we really need it to be defined in every module separately? Maybe you could use the list of subprojects (I proposed above) to do it in a more generic way?
I tried to make that work, but ran into problems. First of all, not every subproject has this test task. Therefore, I had to somehow filter the correct project. However, if you do something like below Gradle will throw an exception:

```groovy
subprojects {
  if (it.getTaskByName('test')
    dependsOn "${it.path}:test"
}
```

My Groovy is lackluster and I just couldn't get this to work.
Yes, because only this task will generate the correct `*.exec` files in `build/` which then have to be picked up by the root project. If you have a better solution, feel free to push to this PR!
I will try to tune that tomorrow. Stay tuned.
I'm curious why we need to specify the tool version. Can you get rid of the version and see if it works? If we bump Gradle, we generally want the best jacoco version that works with given Gradle version. Let's try if we can rely on the default.

Not blocking for this PR.
Does this work? I suspect we can run into problems because we cannot guarantee that at this point subproject has any source sets (the sequence / order in which Gradle configures projects).
I looked at the current "mockitoCoverage" implementation and I think that the change is a bit more involved. For example, we should also include ```classDirectories``` from the subprojects.
Let's avoid code duplication. I'm sure @szpak will figure this out.
Let's avoid hardcoding the test task names and coupling with subprojects. 

Something like that should work (please note that I have not tested it :):

```Gradle
task mockitoCoverage(...)

allprojects {
  plugins.withId("java") {
    apply plugin: "jacoco"
    //configure jacoco version, etc.

    mockitoCoverage.dependsOn "test"
    //configure other parts of jacoco plugin or the mockitoCoverage task
  }
}
```
Gradle usually has outdated version of JaCoCo by default (see https://github.com/gradle/gradle/commit/987d86f36a05b9090ab498dfbe77fe3dbc0f8e39 - 6 months after the release of 0.7.9 - thanks to the PR from the JaCoCo committer). In addition Mockito tended (in the past) to do not use the newest Gradle version.
Therefore, it's redundant right now, but it can be needed once 0.7.10 is released. As a result, having it just one place would be probably enough to be able to uncomment one line in `coverage.gradle`.
Sure, if not I will ask the Gradle 1.x core developer for help :).

WiP.
It was more tricky and took much more time that I expected... I did it in 3 somehow different ways and in the end I left the simplest version (with one file gathering all coverage data). Feel free to comment and to enhance.
@mockitoguy Do you have a good idea how to get rid of it without changing the main Gradle configuration in Mockito to much?

Subtasks doesn't have `Test` tasks enhances automatically due to the applying order.
Not sure. I don't fully see the problem with ordering.
It looks somewhat confusing that "allprojects" section is within the body of the "mockitoCoverage" task configuration.
Let's get rid of "for client" - I don't even know what it means :)
Does it actually generate html and xml? In the past the task could only generate only one kind of report.
Is this line needed?
It's caused by the fact that the root project in Mockito is not an empty parent project, but mockito-core in fact (which also generates coverage data).
According [documentation](https://docs.gradle.org/current/userguide/jacoco_plugin.html#sec:jacoco_specific_task_configuration):
> The JaCoCo plugin adds a JacocoTaskExtension extension to all tasks of type Test. This extension allows the configuration of the JaCoCo specific properties of the test task.

It doesn't happen for subprojects here. I needed to call "applyTo" manually. I didn't see it in multiprojects with "normal" empty parent/root project.
Yes, it is (it's enabled in the configuration)
.
For me the HTML link is enough. I don't know why the the link do XML was introduced. @TimvdLippe do you remember?
Rather yes. There is:
```
dependencies {
    testCompile "org.mockito:mockito-core:$version"
    testCompile project(path: ':', configuration: 'testUtil')
    testCompile libraries.junit4
    testCompile libraries.assertj
}
```
in that file below.
That is used by CodeCov afaik
Yes, but do they use also a link displayed in the console? :)
Oh in that manner. I doubt it, but does it harm by reporting it?
Should we also take into account the Kotlin projects, as they are also generating coverage?
The groovy/kotlin plugins applies the `java` plugin under the hood.
Ah okay :+1: 

Patch that resolved the issue:
diff --git a/gradle/root/coverage.gradle b/gradle/root/coverage.gradle
index 9196ad2cd4..a0f0448562 100644
--- a/gradle/root/coverage.gradle
+++ b/gradle/root/coverage.gradle
@@ -1,15 +1,27 @@
-apply plugin: 'jacoco'
+//TODO: Standard JaCoCo coverage doesn't work in Android module
+task mockitoCoverage(type: JacocoReport) {
 
-// TODO later include subprojects, possible ideas https://gist.github.com/aalmiray/e6f54aa4b3803be0bcac
+    allprojects { currentProject ->
+        plugins.withId("java") {
+            mockitoCoverage.sourceSets currentProject.sourceSets.main
 
-jacoco {
-    toolVersion = "0.7.9"
-}
+            apply plugin: "jacoco"
+
+            jacoco {
+                toolVersion = '0.7.9' //Gradle 4.2.1 uses 0.7.8 by default
 
-task mockitoCoverage(type: JacocoReport, dependsOn: "test") {
-    executionData files("${buildDir}/jacoco/test.exec")
+                if (currentProject != rootProject) {   //already automatically enhanced in mockito main project as "java" plugin was applied before
+                    applyTo test
+                }
+            }
+
+            currentProject.tasks.withType(Test) { testTask ->
+                mockitoCoverage.dependsOn testTask
+            }
+        }
+    }
 
-    sourceSets sourceSets.main
+    executionData(file("$buildDir/jacoco/test.exec"))
 
     reports {
         xml.enabled true
@@ -27,8 +39,8 @@ task mockitoCoverage(type: JacocoReport, dependsOn: "test") {
     }
 
     doLast {
-        println "Jacoco report for client created: file://${reports.xml.destination.toURI().path}"
+        logger.lifecycle "Jacoco HTML created: file://${new File(reports.html.destination, "index.html").toURI().path}"
     }
 }
 
-task coverageReport(dependsOn: ["test", "mockitoCoverage"]) {}
+task coverageReport(dependsOn: ["mockitoCoverage"]) {}
diff --git a/subprojects/extTest/extTest.gradle b/subprojects/extTest/extTest.gradle
index cd827bcc2f..35d2b9863d 100644
--- a/subprojects/extTest/extTest.gradle
+++ b/subprojects/extTest/extTest.gradle
@@ -1,3 +1,5 @@
+apply from: "$rootDir/gradle/dependencies.gradle"
+
 apply plugin: 'java'
 description = "End-to-end tests for Mockito and its extensions."
 
diff --git a/subprojects/kotlinTest/kotlinTest.gradle b/subprojects/kotlinTest/kotlinTest.gradle
index b3a22ae929..d7d349951e 100644
--- a/subprojects/kotlinTest/kotlinTest.gradle
+++ b/subprojects/kotlinTest/kotlinTest.gradle
@@ -18,4 +18,3 @@ dependencies {
     testCompile 'org.jetbrains.kotlin:kotlin-stdlib:1.1.4-3'
     testCompile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.18'
 }
-


Test patch: