skip to content

How do you enforce a minimum coverage threshold in Maven so the build fails when coverage is too low?

level: middleimportance: must knowfreq 60%

answer

  1. check goal fails build
  2. rule = element + limits
  3. counter/value/minimum
  4. COVEREDRATIO is 0..1 decimal
  5. bind to verify
  6. haltOnFailure=false to warn

basics

~10 s

Bind JaCoCo's check goal and define <rules> with a <limit> — e.g. counter LINE, value COVEREDRATIO, minimum 0.80. If coverage is below the limit, the build fails (typically in the verify phase).

solid answer

~40 s

The `check` goal enforces coverage gates. You configure one or more `<rule>` elements; each has an `<element>` scope (BUNDLE, PACKAGE, CLASS, METHOD) and `<limit>` entries. Each limit names a `<counter>` (INSTRUCTION, LINE, BRANCH, METHOD, CLASS, COMPLEXITY), a `<value>` (COVEREDRATIO, MISSEDCOUNT, COVEREDCOUNT, TOTALCOUNT, MISSEDRATIO), and a `<minimum>` or `<maximum>`. For a ratio like COVEREDRATIO you give a decimal (0.80 = 80%). If any limit is violated the goal fails the build. Bind it to `verify` so it runs after integration tests, and make sure a `report`/exec exists first. You can also use `<excludes>` for generated classes. The classic combo: BUNDLE-level LINE COVEREDRATIO minimum 0.80 plus BRANCH minimum 0.70.

code

xml · 19 lines
xml
<execution>
  <id>jacoco-check</id>
  <phase>verify</phase>
  <goals><goal>check</goal></goals>
  <configuration>
    <rules>
      <rule>
        <element>BUNDLE</element>
        <limits>
          <limit>
            <counter>LINE</counter>
            <value>COVEREDRATIO</value>
            <minimum>0.80</minimum>
          </limit>
        </limits>
      </rule>
    </rules>
  </configuration>
</execution>

go deeper

for a junior

Knows the check goal can fail the build below a threshold.

for a middle

Can write a rule with element/counter/value/minimum and bind it to verify.

for a senior

Combines BUNDLE + CLASS rules, uses excludes for generated code, and chooses ratio vs count for ratchet strategies.

for a principal

Sets org-wide coverage policy (which counters, excludes, ratchet vs fixed) and decides haltOnFailure posture per pipeline stage.

## Why check exists `report` is descriptive. To make coverage a build gate you need the `check` goal, which compares measured coverage against rules and **fails the build** when violated. ## Anatomy of a rule Each `<rule>` has: - **`<element>`** — the scope the rule applies to: `BUNDLE` (whole module, the default), `PACKAGE`, `CLASS`, `METHOD`, `SOURCEFILE`. - one or more **`<limit>`** entries. Each `<limit>` has: - **`<counter>`** — what to count: `INSTRUCTION`, `LINE`, `BRANCH`, `METHOD`, `CLASS`, `COMPLEXITY`. - **`<value>`** — the metric: `COVEREDRATIO` (0..1), `MISSEDRATIO`, `COVEREDCOUNT`, `MISSEDCOUNT`, `TOTALCOUNT`. - **`<minimum>`** and/or **`<maximum>`** — the threshold (a decimal like `0.80` for ratios, integer for counts). ## Example ```xml <execution> <id>jacoco-check</id> <phase>verify</phase> <goals><goal>check</goal></goals> <configuration> <rules> <rule> <element>BUNDLE</element> <limits> <limit> <counter>LINE</counter> <value>COVEREDRATIO</value> <minimum>0.80</minimum> </limit> <limit> <counter>BRANCH</counter> <value>COVEREDRATIO</value> <minimum>0.70</minimum> </limit> </limits> </rule> <rule> <element>CLASS</element> <limits> <limit> <counter>LINE</counter> <value>COVEREDRATIO</value> <minimum>0.50</minimum> </limit> </limits> <excludes> <exclude>com.acme.generated.*</exclude> </excludes> </rule> </rules> </configuration> </execution> ``` ## Phase binding Bind to **`verify`** so check runs after both Surefire (unit) and Failsafe (integration) and after the relevant report/exec is available. check itself needs the `.exec` data — prepare-agent must have run earlier in the same build. ## Behavior on violation A violated limit prints e.g. `Rule violated for bundle X: lines covered ratio is 0.62, but expected minimum is 0.80` and fails the build (BUILD FAILURE). Use `<haltOnFailure>false</haltOnFailure>` to warn without failing. ## Practical tips - Per-CLASS rules catch newly-added under-tested classes that a BUNDLE average would hide. - Use `<excludes>` (or the agent's `<excludes>`) for generated code, DTOs, config. - Counts (MISSEDCOUNT maximum) are useful for ratchet strategies.

  • Why might a BUNDLE-level 80% rule still let a completely untested class slip in?
    BUNDLE averages across the whole module, so a large well-tested codebase masks one untested class. Add a CLASS-level rule (e.g. minimum 0.50) to catch per-class gaps.
  • How do you make check warn but not fail the build?
    Set <haltOnFailure>false</haltOnFailure> in the check configuration; it logs violations without failing.
  • What value would you use to express 85% as a minimum line coverage?
    counter LINE, value COVEREDRATIO, minimum 0.85 — ratios are decimals between 0 and 1.

saying these in an interview costs you the question

  • Writing minimum as 80 instead of 0.80 for a ratio (treated as 8000%, always fails)
  • Binding check before tests/exec exist
  • Relying only on BUNDLE averages so per-class gaps hide
  • Thinking report enforces thresholds

context