skip to content

What does the jacocoTestCoverageVerification task do in a Gradle build, and how do you make it fail the build when coverage drops below a threshold?

level: juniorimportance: must knowfreq 60%

answer

  1. jacocoTestCoverageVerification = gate, not report
  2. violationRules { rule { limit { minimum } } }
  3. reads build/jacoco/test.exec
  4. not in check by default — wire it
  5. fail = non-zero exit

basics

~10 s

It's a JaCoCo plugin task that checks coverage against rules you define in violationRules. If coverage is below the configured minimum, the task fails, which fails the build.

solid answer

~40 s

The `jacocoTestCoverageVerification` task is added by the `jacoco` plugin alongside `jacocoTestReport`. You configure it with a `violationRules` block containing one or more `rule {}` entries; each rule has `limit { ... }` clauses specifying a `counter`, a `value`, and a `minimum` (or `maximum`). When you run the task, JaCoCo reads the `.exec` execution data, computes the metric, and if any rule is violated the task throws and the build fails with a non-zero exit code. By default the task is NOT part of `check`, so you typically wire it in with `check.dependsOn(jacocoTestCoverageVerification)` so CI enforces the gate. A common rule: instruction coverage minimum 0.80 across the whole bundle.

code

kotlin · 17 lines
kotlin
plugins { jacoco }

tasks.jacocoTestCoverageVerification {
    violationRules {
        rule {
            limit {
                counter = "INSTRUCTION"
                value = "COVEREDRATIO"
                minimum = "0.80".toBigDecimal()
            }
        }
    }
}

tasks.named("check") {
    dependsOn(tasks.named("jacocoTestCoverageVerification"))
}

go deeper

for a junior

Know it exists, that it's a separate task from the report, and that violationRules with a minimum makes the build fail.

for a middle

Be able to write the violationRules block from memory and explain wiring it into check via dependsOn.

for a senior

Discuss counter/value choices, why it's opt-in, and how it fits a CI quality gate.

for a principal

Frame coverage gates as policy, including how to roll thresholds out org-wide without blocking teams overnight.

## What it is The `jacoco` Gradle plugin adds two main tasks: `jacocoTestReport` (generates HTML/XML/CSV reports) and `jacocoTestCoverageVerification` (a *gate* that fails the build when coverage rules are not met). This leaf is about the verification task specifically. ## Where the data comes from JaCoCo instruments your bytecode during `test` and writes execution data to a `.exec` file (default `build/jacoco/test.exec`). The verification task consumes that `.exec` plus the compiled classes to compute coverage, exactly like the report task — but instead of rendering output, it asserts thresholds. ## Configuring violationRules You configure the task's `violationRules { }` block. Each `rule { }` evaluates one or more `limit { }` clauses. A limit has three parts: - `counter` — what to count: `INSTRUCTION` (default), `LINE`, `BRANCH`, `COMPLEXITY`, `METHOD`, `CLASS`. - `value` — how to express it: `COVEREDRATIO` (default, 0.0–1.0), `TOTALCOUNT`, `COVEREDCOUNT`, `MISSEDCOUNT`, `MISSEDRATIO`. - `minimum` / `maximum` — the bound, as a `BigDecimal` (use `"0.80".toBigDecimal()` in Kotlin DSL). ## Failing the build If any active rule is violated, the task throws a `GradleException` and the build exits non-zero. That is the whole point — it turns a soft report into a hard quality gate. ## Wiring into check Crucially, `jacocoTestCoverageVerification` is **not** a dependency of `check` by default (unlike, say, `test`). So nothing enforces it unless you opt in: ```kotlin tasks.named("check") { dependsOn(tasks.named("jacocoTestCoverageVerification")) } ``` This makes `./gradlew check` (and therefore CI) run the gate. The verification task already depends on `test` transitively for its input data, so ordering is handled. ## Example ```kotlin tasks.jacocoTestCoverageVerification { violationRules { rule { limit { counter = "INSTRUCTION" value = "COVEREDRATIO" minimum = "0.80".toBigDecimal() } } } } ```

  • Why isn't jacocoTestCoverageVerification run automatically by check?
    The plugin deliberately does not wire it into check, because coverage thresholds are a project policy decision. Teams opt in explicitly via dependsOn so they choose when the gate becomes blocking.
  • Where does the coverage data the verification task reads come from?
    From the JaCoCo execution data file (build/jacoco/test.exec) produced when the test task runs with the JaCoCo agent attached, combined with the compiled class files.

saying these in an interview costs you the question

  • Claiming check already runs the verification task automatically — it does not by default.
  • Confusing jacocoTestCoverageVerification (the gate) with jacocoTestReport (the report).
  • Saying it reads the HTML report — it reads the .exec execution data plus classes.

context