code-coverage
| Type | Skill |
| Plugin | awl-android · v0.0.19 |
| Invoke | /awl-android:code-coverage |
| Tools | Read, Write, Edit, Bash, Glob, Grep, WebFetch |
| Source | plugins/awl-android/skills/code-coverage/SKILL.md |
When Claude uses it
Section titled “When Claude uses it”Sets up Kotlin code coverage with Kover for an Android or KMP project the way the AWL templates do it (per-module custom variant, koverPrintCoverageCustom, exclusion filters, optional threshold, enable_coverage on the android CI component). Use when asked to “add code coverage”, “set up Kover”, “measure test coverage”, “generate a coverage report”, “add a coverage gate or threshold”, “show coverage on the MR”, or when koverPrintCoverageCustom is missing in CI. NOT for JaCoCo, for iOS coverage (xcov via the ios-fastlane component), or for writing the tests themselves.
Trigger phrases: add code coverage · set up Kover · measure test coverage · generate a coverage report · add a coverage gate or threshold · show coverage on the MR
Definition
Section titled “Definition”Each module (androidApp, shared) gets its own Kover config bound to a coverage variant named custom, with exclusion filters. Kover does not aggregate across modules at the root, so each module reports independently and one *Custom task fans out to all of them. No JaCoCo.
Workflow
Section titled “Workflow”| Step | Do | Where |
|---|---|---|
| 1 | Find the variant each module tests against | this file |
| 2 | Add the plugin to the catalog and root build | this file |
| 3 | Configure androidApp |
templates/androidApp-kover.kts |
| 4 | Configure shared |
templates/shared-kover.kts |
| 5 | Propose project-specific exclusions | this file |
| 6 | Generate reports and verify | this file |
| 7 | Wire into CI | templates/gitlab-coverage.yml |
1. Prerequisites
Section titled “1. Prerequisites”- Modules to cover. Default:
androidApp(Compose UI plus ViewModels) andshared(KMP logic). - The variant each module’s unit tests run against, because it binds the coverage variant:
androidApp: the Android build variant under test. The AWL templates usedevDebug. Find it with./gradlew :androidApp:tasks --all | grep -i 'test.*UnitTest'.shared: the Android target of the KMP block,androidin the templates.
- A version catalog at
gradle/libs.versions.toml. Inline versions if the project has none. - Kover version: the templates pin
0.9.9. Check https://github.com/Kotlin/kotlinx-kover/releases for newer.
2. Plugin declaration
Section titled “2. Plugin declaration”gradle/libs.versions.toml:
[versions]kover = "0.9.9"
[plugins]kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }Root build.gradle.kts, so modules can opt in:
alias(libs.plugins.kover) apply false3. Configure androidApp
Section titled “3. Configure androidApp”Apply alias(libs.plugins.kover) in plugins {} and paste the kover {} block from templates/androidApp-kover.kts. The variant name custom is the core of the setup: declaring the same name in every module lets one task (koverHtmlReportCustom) cover the whole app. Bind it with addWithDependencies("devDebug"), or whatever step 1 found.
The template excludes *di.*, *ui.theme.*, *ComposableSingletons*, and everything annotated with androidx.compose.ui.tooling.preview.Preview. A Compose Multiplatform app also excludes org.jetbrains.compose.ui.tooling.preview.Preview.
4. Configure shared
Section titled “4. Configure shared”Same pattern, from templates/shared-kover.kts, bound to the Android target with addWithDependencies("android"). A shared module that only holds business logic excludes *di.*. When shared also holds Compose UI (Compose Multiplatform), reuse the full androidApp exclusion list including both Preview annotations.
5. Suggesting exclusions
Section titled “5. Suggesting exclusions”Start from the generic exclusions in the templates. Then look at the project and propose more only where they clearly apply:
| If the project has | Suggest excluding | Pattern |
|---|---|---|
| Generated protobuf, wire, or contracts modules | the generated package | *.contracts.* |
| Generated navigation (KSP, compose-destinations) | nav glue and generated args | *.ui.NavGraph, *NavArgsGettersKt |
| Debug-only UI (debug drawer, dev screens) | debug UI | *.ui.debug.* |
| Custom preview annotations | annotate-by that annotation | annotatedBy("com.example.DevicesPreview") |
| Sample or fake data baked into the build | those classes | *.SampleData, *Fake* (check it does not hide real code) |
Confirm the list with the user before adding it, then check each pattern actually disappears from the HTML report.
6. Generate reports and verify
Section titled “6. Generate reports and verify”The suffix is the capitalised variant name:
./gradlew koverPrintCoverageCustom # console summary, all modules./gradlew koverHtmlReportCustom # HTML./gradlew koverXmlReportCustom # XML for CIKover runs the bound variant’s unit tests itself, so no separate test<Variant>UnitTest call is needed. Open the HTML reports and confirm real percentages and absent excluded packages:
open androidApp/build/reports/kover/reportCustom/index.htmlopen shared/build/reports/kover/reportCustom/index.htmlReport the headline percentage per module to the user. Outputs: <module>/build/reports/kover/reportCustom/index.html and <module>/build/reports/kover/reportCustom.xml. The console line reads application line coverage: NN%, once per module.
The template’s fastlane lane android test runs the same pair (koverXmlReportCustom koverPrintCoverageCustom).
7. Wire into CI
Section titled “7. Wire into CI”On GitLab with the AWL android component, set enable_coverage: true on the include. The testUnit job then appends the two Kover tasks to the same Gradle run as test_task, parses the line percentage for the MR, and keeps every module’s reportCustom.xml as an artifact. Because Kover prints one line per module and GitLab takes the last match, the MR number shows the last module printed; the XML artifacts hold all of them. Keep the custom binding aligned with test_task (devDebug with testDevDebugUnitTest).
Projects that cannot use the component get a standalone job from templates/gitlab-coverage.yml. GitHub repos use templates/ci-publish-tests.yml, which posts per-module coverage to the PR.
A threshold is optional; the templates ship none. Enforce one either via the GitLab project setting “coverage must not decrease” or with Kover’s own rule (kover { reports { verify { rule { minBound(80) } } } }) plus koverVerifyCustom in the job.
Gotchas
Section titled “Gotchas”- The variant name has to match across modules; mismatched names mean separate tasks.
- Exclusion globs are fully qualified class patterns:
*di.*,*ComposableSingletons*,ch.example.contracts.*. - KMP
sharedbinds to the Android target only. Other KMP targets are not covered by this variant. - No root-level merge: report each module independently.
- iOS coverage in a KMP repo comes from xcov in the
ios-fastlanecomponent, not from Kover.

