skip to content

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%

answer

  1. element = BUNDLE/PACKAGE/CLASS/SOURCEFILE/METHOD
  2. CLASS element = per-class enforcement
  3. excludes = FQN globs, not regex
  4. layer BUNDLE headline + CLASS floor
  5. rule excludes ≠ classDirectories excludes

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.

solid answer

~40 s

Each `rule { }` has an `element` that controls the scope the `limit`s are evaluated against. Default is `BUNDLE` — the whole aggregated project, so one global number. Setting `element = "CLASS"` makes the rule evaluate *every class independently*: each class must individually meet the limit, and the build fails listing each violator. Other elements are `PACKAGE`, `METHOD`, and `SOURCEFILE`. With finer elements you almost always need `excludes` — a list of fully-qualified name patterns (e.g. `"*.generated.*"`, `"*MapperImpl"`) — because generated, DTO, or config classes would otherwise fail an unrealistic per-class bar. So a typical strict gate is `element="CLASS", excludes=[...], limit { minimum=0.6 }` combined with a softer `BUNDLE` headline rule.

code

kotlin · 13 lines
kotlin
tasks.jacocoTestCoverageVerification {
    violationRules {
        rule { // headline
            element = "BUNDLE"
            limit { minimum = "0.80".toBigDecimal() }
        }
        rule { // per-class floor
            element = "CLASS"
            excludes = listOf("*.dto.*", "*Application", "*MapperImpl")
            limit { minimum = "0.50".toBigDecimal() }
        }
    }
}

go deeper

for a junior

Know the default element is BUNDLE and that CLASS exists for per-class checks.

for a middle

Configure element=CLASS with excludes and explain glob (not regex) semantics.

for a senior

Design a layered BUNDLE + CLASS gate and justify the excludes list.

for a principal

Standardize an excludes policy across modules and decide rule excludes vs computation excludes for accurate org metrics.

## What element controls A single `rule { }` is evaluated against a *set of coverage elements*. The `element` property picks the granularity: - `BUNDLE` (default) — the entire project/module as one unit. One pass/fail number. - `PACKAGE` — each package independently. - `CLASS` — each class independently. - `SOURCEFILE` — each source file independently. - `METHOD` — each method independently (rarely used; very noisy). The finer the element, the more separate checks the rule performs. With `element = "CLASS"`, a rule with `minimum = 0.6` means *every class* must hit 60%, and the failure report enumerates each class below the bar. ## Why excludes matter at fine granularity A per-class rule is brittle without filtering. Generated mappers, `@Data`/Lombok-style classes, DTOs, `*Application` bootstrap classes, and enums often have trivially-untestable or auto-generated code. JaCoCo rules support `excludes` (and `includes`) lists of FQN glob patterns evaluated against the element name: ```kotlin rule { element = "CLASS" excludes = listOf( "*.generated.*", "*MapperImpl", "*Application", "*.dto.*" ) limit { counter = "INSTRUCTION" value = "COVEREDRATIO" minimum = "0.60".toBigDecimal() } } ``` Note: rule-level `excludes` filter which *elements* the rule applies to. This is different from `classDirectories` / `afterEvaluate { ... exclude(...) }` filtering, which removes classes from the coverage computation entirely. Rule excludes are the targeted way to relax a single gate without distorting the headline number. ## Pattern: layered rules A mature setup uses two rules in one `violationRules` block: a lenient `BUNDLE` headline (e.g. 80% instructions) plus a per-`CLASS` floor (e.g. 50%) with excludes, so a single hot, untested class can't hide behind a high average. ## Gotcha Element patterns use `.`-qualified names with `*`/`?` globs, not regex. `excludes = listOf("com.acme.gen.*")` matches that package's classes.

  • What's the difference between rule-level excludes and excluding classes from classDirectories?
    Rule excludes only stop a specific rule from applying to those elements — the classes still appear in reports and other rules/the headline number. classDirectories excludes remove the classes from coverage computation entirely, changing every number.
  • Why combine a BUNDLE rule with a per-CLASS rule?
    The BUNDLE average can stay high while one critical class is 0% covered. A per-CLASS floor catches that hidden gap, so neither a good average nor a single rotten class slips through.

saying these in an interview costs you the question

  • Treating excludes patterns as regular expressions instead of Ant-style globs.
  • Putting a strict per-CLASS minimum without any excludes, then fighting generated-class failures.
  • Assuming element defaults to CLASS — it defaults to BUNDLE.

context