Kotlin / kotlinx-kover

Apache License 2.0
1.25k stars 46 forks source link

How can App module include `kover(project(":lib"))` for a large number of sub-modules? #594

Open trietbui85 opened 1 month ago

trietbui85 commented 1 month ago

Describe what you would like to clarify about Kover My project is multi modules, multi flavors, with the following structure

RootDir
- app  -> with 2 flavors: Dev & Google
- core
- - lib1 -> Kotlin module
- - lib2 -> Android module
- - ... and so on, might be up to 100 modules
- feature
- - feat1 -> Android module, depend on Core:lib1
- - feat2 -> Android module, depend on Core:lib1 & lib2
- - ... and so on, might be up to 100 modules

(My real project has more than 200 sub-modules)

App depends on all Feature modules, but not Core module. Therefore, its build.gradle with Kover block would be:

dependencies {
    implementation project(':feature:feat1')
    implementation project(':feature:feat2')
    // ... and so on, might be up to 100 modules 

    kover(project(":feature:feat1"))
    kover(project(":feature:feat2"))
    // ... and so on, might be up to 100 modules 
}

So, my question:

  1. Is there any less boilerplate to call kover(project(":feature:xxx"))? I don't think we should call it up to 100 times to include all Feature modules.
  2. In App level build.gradle, do I need to call kover(project(":core:xxx")) for Core modules? Or should it call kover(project(":core:xxx")) in the Feature module instead?
  3. What happens if I don't call kover(project(":feature:xxx")) in App module? I still see the coverage report, but is it the coverage of all modules in project, or just App module? 🤔
shanshin commented 1 month ago

Hi,

  1. Is there any less boilerplate to call kover(project(":feature:xxx"))? I don't think we should call it up to 100 times to include all Feature modules.

you can use the functions to iterate through the projects in the build like subprojects or allprojects (depends on where you write it down). e.g.

dependencies {
    allprojects {
        kover(this)
    }
}

*at the same time, it is important that the Kover plugin is applied in all projects that will be used in kover(this)

  1. In App level build.gradle, do I need to call kover(project(":core:xxx")) for Core modules? Or should it call kover(project(":core:xxx")) in the Feature module instead?

This approach is recommended: you select one specific project in which the task of generating the merged report will be located. We will call such a project a merging project. In it, you must specify the kover dependencies (point above) for all projects whose source code should be included in the report (and in which test tasks should be launched).

For example, you decided that :app would be a merging project. You add dependencies to other projects using allprojects. To generate a report, you will need to call a task such as :app:koverHtmlReportDev. In this case, the report will contain classes from :app and all projects specified in the kover dependency.

  1. What happens if I don't call kover(project(":feature:xxx")) in App module? I still see the coverage report, but is it the coverage of all modules in project, or just App module? 🤔

if you do not specify the kover(project(":feature:xxx")) dependency, then classes from :feature:xxx will not be included in the merged report and you will see coverage only for classes from :app.


If your build consists of mixed Kotlin and Android projects, then I recommend trying to use the 0.8.0-Beta2 version of the Kover plugin.

shanshin commented 1 month ago

Such multi-project builds are very useful for obtaining use-cases of using the Kover plugin. Could you please share your opinion, for your project, would it be convenient for you if the merged report (in :app project) included all the projects specified in the implementation(project(...)) dependency?

And so they were added recursively, e.g. : :app <- implementation(project(':feature:feat1')) <- implementation(project(':core:lib1')), thus, the merged report in :app would include classes from :feature:feat1 and :core:lib1

woodii commented 4 days ago

hi @shanshin. Here is my feedback to your question above, since a have a similar usecase.

Could you please share your opinion, for your project, would it be convenient for you if the merged report (in :app project) included all the projects specified in the implementation(project(...)) dependency?

In my opinion this would be very useful. We currently need one merged report that we can pass to sonarqube. I can't think of a project where i would exclude modules which are implemented.

This feature would furthermore solve one problem of mine. We have a multi-module project which results in a few different apps. Depending on the buildvariant different modules are implemented. When using allprojects without any sort of filtering based on the buildvariant this results on too many modules in the report for that specific app. Furthermore the described idea using allprojects inside dependency block inside the app's build.gradle does not work for me.

I currently use the following workaround:

List<String> exclusions = List.of(":auth", ":core", ":features")
List<String> appAExclusions = List.of(":features:featureb")
List<String> appBExclusions = List.of(":features:featurea")

Boolean isA = buildVariant.toLowerCase().startsWith("appa")
List<String> applicableExclusions = generalExclusions + (isA ? appAExclusions : appBExclusions)

return rootProject.subprojects
    .findAll {
        !applicableExclusions.contains(it.buildTreePath)
    }.asList()

I then iterate over this list inside :app's dependency block and do kover(project)

Do you have any other suggestions which feel less like an hacky workaround? ;)