skip to content

Coverage Verification Rules

Failing the build with jacocoTestCoverageVerification when coverage falls below a rule's limit, wired into check. Interviewers ask because a report nobody enforces changes nobody's behavior.

on this pageshow

questions

5

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

open as a page

Inside a violationRules limit, what do the counter and value properties control, and how do they change what gets enforced?

level: middleimportance: must knowfreq 50%

basics

~10 s

counter picks the metric (INSTRUCTION, LINE, BRANCH, COMPLEXITY, METHOD, CLASS). value picks how it's measured (COVEREDRATIO, MISSEDCOUNT, TOTALCOUNT, etc.). Together with minimum/maximum they define the rule.

open as a page

How does the 'element' scope in a JaCoCo violation rule work, and how would you enforce a per-class minimum while excluding generated code?

level: middleimportance: should knowfreq 38%

basics

~10 s

A rule's element sets the granularity it's applied at: BUNDLE (whole project, default), PACKAGE, CLASS, METHOD, or SOURCEFILE. Use element = CLASS to enforce per-class, and excludes to drop generated classes.

open as a page

A team wants per-PR coverage enforcement that doesn't block on legacy code but holds the bar on new work. How do you structure jacocoTestCoverageVerification rules, and how do you wire and stage the gate in CI?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Use multiple rules: a lenient BUNDLE floor everyone passes today, plus stricter per-CLASS/per-PACKAGE rules with includes/excludes targeting new or critical areas. Wire it into check, and ratchet thresholds up over time.

open as a page

Explain the task input/output relationship of jacocoTestCoverageVerification: what it depends on, what makes it up-to-date, and why running it alone can still execute tests.

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

The verification task consumes the test execution data (.exec) and the compiled classes as inputs. Because it needs that data, it's wired to run after test, so invoking it triggers test first. It's up-to-date when inputs and rules are unchanged.

open as a page