skip to content

Testing & Code Quality

Running tests and enforcing quality gates inside the lifecycle: Surefire and Failsafe, JaCoCo coverage, static-analysis plugins, and Enforcer rules. Interviewers probe here to learn whether your build fails on bad code or merely reports it.

on this pageshow

explore

questions

27

How do you set up JaCoCo to measure code coverage in a Maven build, and what does the prepare-agent goal actually do?

level: juniorimportance: must knowfreq 70%

answer

  1. prepare-agent sets argLine
  2. javaagent attaches to test JVM
  3. jacoco.exec output
  4. Surefire/Failsafe read ${argLine}
  5. @{argLine} avoids clobber

basics

~10 s

Add the jacoco-maven-plugin and bind its prepare-agent goal. prepare-agent sets a property (argLine) that adds the JaCoCo Java agent to the JVM running your tests, so it records which lines execute.

solid answer

~40 s

You add jacoco-maven-plugin to the build and bind the prepare-agent goal (usually to the initialize/early phase via the default execution). prepare-agent does not run tests itself; it configures the JaCoCo Java agent and exposes its JVM options in a Maven property named argLine by default. Surefire/Failsafe pick up ${argLine} and prepend it to the test JVM command line, so the agent instruments classes on the fly and writes execution data to target/jacoco.exec. The key gotcha: if you also set argLine manually (e.g. for -Xmx), you must include @{argLine} or set it via a property so you don't clobber JaCoCo's value. Then bind the report goal (typically to the test/verify phase) to turn jacoco.exec into HTML/XML.

code

xml · 16 lines
xml
<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>0.8.12</version>
  <executions>
    <execution>
      <id>jacoco-prepare</id>
      <goals><goal>prepare-agent</goal></goals>
    </execution>
    <execution>
      <id>jacoco-report</id>
      <phase>test</phase>
      <goals><goal>report</goal></goals>
    </execution>
  </executions>
</plugin>

go deeper

for a junior

Knows you add jacoco-maven-plugin and bind prepare-agent + report to get a coverage report.

for a middle

Understands prepare-agent populates the argLine property that Surefire/Failsafe consume to attach the agent.

for a senior

Diagnoses empty-coverage problems from argLine clobbering and uses @{argLine} or a custom propertyName.

for a principal

Standardizes coverage wiring across many repos via a parent/BOM, sets propertyName conventions and avoids per-module argLine hacks.

## What code coverage is Code coverage measures which lines/branches of your source code were actually executed while your tests ran. JaCoCo (Java Code Coverage) is the de-facto tool; the `jacoco-maven-plugin` integrates it into Maven. ## How JaCoCo collects data JaCoCo works as a **Java agent** — a `-javaagent:...` JVM argument that instruments bytecode as classes load and records execution counts. To get coverage you must inject that agent into the JVM that runs your tests. ## The prepare-agent goal The `prepare-agent` goal does NOT run tests. Its job is to compute the correct `-javaagent` string and store it in a Maven property. By default that property is named **`argLine`**. Why `argLine`? Because Maven Surefire (unit tests) and Failsafe (integration tests) read a configuration property called `argLine` and prepend it to the forked test JVM's command line. So by writing into `argLine`, JaCoCo transparently attaches itself to the test JVM. The agent writes raw execution data to **`target/jacoco.exec`** by default. ## Wiring it up ```xml <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.12</version> <executions> <execution> <id>prepare-agent</id> <goals><goal>prepare-agent</goal></goals> </execution> <execution> <id>report</id> <phase>test</phase> <goals><goal>report</goal></goals> </execution> </executions> </plugin> ``` The `prepare-agent` execution binds by default to the **initialize** phase, so it runs before tests. ## The argLine clobbering trap If you set `argLine` yourself in Surefire/Failsafe config (e.g. `-Xmx512m`), you overwrite JaCoCo's value and coverage silently becomes empty. Fix by referencing the late-evaluated property: ```xml <configuration> <argLine>@{argLine} -Xmx512m</argLine> </configuration> ``` The `@{...}` syntax forces Surefire to resolve the property late, after prepare-agent has set it. Alternatively, point JaCoCo at a different property via `<propertyName>surefireArgLine</propertyName>` and reference that. ## Output - `target/jacoco.exec` — binary execution data - `target/site/jacoco/` — HTML report (after the `report` goal)

  • Your coverage report is empty (0%) even though tests pass. What's the most likely cause?
    Something overwrote the JaCoCo argLine — e.g. a hardcoded <argLine> in Surefire config without @{argLine}, so the agent never attached. Also check forkCount/tests actually ran.
  • What property does prepare-agent write into and why that name?
    By default the 'argLine' property, because Surefire and Failsafe automatically prepend ${argLine} to the forked test JVM command line.

prepare-agent is like clipping a fitness tracker onto the JVM before it goes for its run — it doesn't run, it just makes sure the tracker is recording.

saying these in an interview costs you the question

  • Saying prepare-agent runs the tests
  • Thinking JaCoCo statically modifies source/class files on disk
  • Not knowing about the argLine clobbering problem

context

open as a page

How do you enforce a minimum Maven and Java version, and what does the version range syntax mean?

level: juniorimportance: must knowfreq 50%

basics

~10 s

Use the requireMavenVersion and requireJavaVersion rules with a version range. For example [3.9,) means '3.9 or higher'. The build fails if the running Maven or JDK is outside the range.

open as a page

What is the Maven Enforcer Plugin and what problem does it solve?

level: juniorimportance: must knowfreq 55%

basics

~10 s

The Enforcer Plugin lets you declare build rules (like a minimum Maven or Java version) and fails the build automatically when they are violated, so problems are caught early instead of at runtime.

open as a page

What is the Maven Failsafe plugin and how does it differ from the Surefire plugin?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Failsafe runs integration tests; Surefire runs unit tests. Surefire fails the build immediately on a test failure, while Failsafe defers failure so teardown always runs.

open as a page

What are static analysis plugins in Maven, and how do you make one (e.g. Checkstyle) actually run during your build?

level: juniorimportance: must knowfreq 55%

basics

~20 s

Static analysis plugins inspect source/bytecode for style and bug issues without running it. You add the plugin to your pom and bind its check goal to a build phase (often verify) inside an <execution> so mvn verify runs it automatically.

open as a page

What is the Maven Surefire plugin and at which point in the build lifecycle does it run unit tests?

level: juniorimportance: must knowfreq 78%

basics

~10 s

Surefire is the Maven plugin that runs your unit tests. It binds to the test phase of the default lifecycle, so tests run automatically during mvn test, package, install, and verify.

open as a page

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

level: middleimportance: must knowfreq 60%

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).

open as a page

Explain why Failsafe defers test failures to the verify goal instead of failing during integration-test.

level: middleimportance: must knowfreq 60%

basics

~10 s

So teardown in post-integration-test always runs. The integration-test goal records results without failing; the verify goal later reads them and fails the build, after cleanup has happened.

open as a page

How do you make a static analysis plugin actually break the build on violations, and what knobs control that behavior?

level: middleimportance: must knowfreq 50%

basics

~10 s

Use the *:check goal (not the report goal) and keep its failure flag on: maven-checkstyle-plugin/maven-pmd-plugin use failOnViolation=true, spotbugs uses failOnError=true. Bind it to a phase like verify so mvn verify fails when violations exist.

open as a page

How do you control which tests Surefire runs using includes/excludes patterns and the -Dtest command-line filter?

level: middleimportance: must knowfreq 70%

basics

~10 s

Configure <includes>/<excludes> patterns in the plugin to match test class names. From the command line, run a subset with -Dtest=ClassName, -Dtest=ClassName#method, or wildcards like -Dtest='*IT' to override the defaults for that run.

open as a page

What is the difference between -DskipTests and -Dmaven.test.skip, and when would you use each?

level: middleimportance: must knowfreq 72%

basics

~20 s

-DskipTests compiles test classes but does not run them. -Dmaven.test.skip=true skips both compiling and running tests. Use skipTests to build a jar fast while keeping tests compilable; use maven.test.skip only when you also want to skip test compilation.

open as a page

What does the dependencyConvergence rule do, and how does it differ from requireUpperBoundDeps?

level: seniorimportance: must knowfreq 45%

basics

~20 s

dependencyConvergence fails the build if a transitive dependency is pulled in at more than one version anywhere in the tree. requireUpperBoundDeps is gentler: it fails only when Maven resolves a version lower than the highest one requested.

open as a page

After execution data is collected, how do you produce a human- and CI-readable coverage report with JaCoCo, and what formats matter?

level: juniorimportance: should knowfreq 50%

basics

~10 s

Bind the report goal. It reads target/jacoco.exec and your compiled classes to emit HTML (for humans) and XML (for CI tools like SonarQube) in target/site/jacoco/.

open as a page

How would you use bannedDependencies and banDuplicateClasses to protect a build?

level: middleimportance: should knowfreq 38%

basics

~20 s

bannedDependencies fails the build if a forbidden artifact appears (e.g. an old logging lib or a vulnerable version), using groupId:artifactId:version patterns with wildcards. banDuplicateClasses fails when the same class exists in two jars on the classpath.

open as a page

How do you control which test classes Failsafe runs, and what are its default include patterns?

level: middleimportance: should knowfreq 40%

basics

~10 s

By default Failsafe picks up classes matching IT, IT, and *ITCase. You override this with <includes>/<excludes> in the plugin config to match your own naming.

open as a page

How do SpotBugs and find-sec-bugs differ from Checkstyle/PMD, and how do you wire them up in Maven?

level: middleimportance: should knowfreq 40%

basics

~20 s

Checkstyle and PMD read your source code; SpotBugs reads compiled bytecode, so it must run after compilation. find-sec-bugs is a SpotBugs plugin adding security bug patterns. You add it under the spotbugs plugin's <plugins> and run spotbugs:check.

open as a page

How do you measure code coverage for integration tests run by the Failsafe plugin, separately from unit tests?

level: seniorimportance: should knowfreq 40%

basics

~10 s

Add a second prepare-agent execution that writes to a separate property (e.g. failsafeArgLine) and a separate destFile (jacoco-it.exec), reference that property in Failsafe's argLine, then run a report on that exec during verify.

open as a page

In a multi-module Maven project, how do you produce a single combined coverage report across all modules?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Create a dedicated 'report' module that depends on all the modules you want measured, and run JaCoCo's report-aggregate goal there. It merges each module's jacoco.exec into one consolidated report.

open as a page

How would you start an external resource before integration tests and guarantee it is torn down afterward using Maven phases?

level: seniorimportance: should knowfreq 45%

basics

~10 s

Bind a start goal to pre-integration-test and a stop goal to post-integration-test. Failsafe runs tests in integration-test without failing, so post-integration-test teardown always executes before verify judges results.

open as a page

How do Spotless and Error Prone fit into a Maven build, and how do they differ from check-only linters?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Spotless is a formatter: spotless:apply rewrites files to a standard format, spotless:check fails the build if they aren't formatted. Error Prone isn't a plugin — it plugs into javac via maven-compiler-plugin and fails compilation on bug-pattern findings.

open as a page

How do you handle false positives and legacy violations across these tools without disabling the gate, and how does baselining/suppression work per tool?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Use each tool's targeted suppression instead of turning failures off: Checkstyle SuppressionFilter XML or @SuppressWarnings, PMD ruleset excludes or @SuppressWarnings("PMD.x"), SpotBugs excludeFilterFile or @SuppressFBWarnings. For legacy code, ratchet with a baseline or maxAllowedViolations so only new violations fail.

open as a page

How does Surefire use forkCount, reuseForks, and parallel to run tests, and how do these interact?

level: seniorimportance: should knowfreq 52%

basics

~20 s

forkCount sets how many separate JVMs run tests at once (e.g. 1C = one per CPU core); reuseForks decides whether a JVM is reused across test classes or recycled. parallel controls threads WITHIN a JVM (methods/classes) via the provider. Forking = process-level, parallel = thread-level.

open as a page

How does Surefire choose between JUnit 4, JUnit 5 (Jupiter), and TestNG providers, and how do you wire JUnit 5 correctly?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Surefire auto-detects the test framework from your test-scoped dependencies and selects a matching provider. For JUnit 5 you add junit-jupiter (API + engine); modern Surefire (2.22+/3.x) discovers the JUnit Platform automatically, so you usually need no extra provider dependency.

open as a page

How would you create a custom Enforcer rule, and how do you roll out Enforcer policy across many projects without crippling developer productivity?

level: principalimportance: should knowfreq 22%

basics

~20 s

Write a class implementing EnforcerRule (or the newer EnforcerRule2/abstract base), package it as a jar, and add it as a plugin dependency so it appears in the rules list. Roll out policy via a shared parent POM, start in warn mode, and use fail=false or skip flags to phase in strictness.

open as a page

How would you design a CI pipeline so unit tests give fast feedback but integration tests still run reliably, using Surefire and Failsafe?

level: principalimportance: nice to knowfreq 30%

basics

~10 s

Run unit tests (Surefire) early via mvn test for fast feedback, then run integration tests (Failsafe) in a later stage via mvn verify with -DskipITs control, so slow ITs don't block quick signal.

open as a page

How would you standardize and govern these static analysis gates across a large multi-module Maven project so rules stay consistent and enforceable?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Define plugin versions and configuration once in a parent POM (pluginManagement) so all modules inherit identical gates, share rule/suppression files (often packaged in a shared artifact), and bind check goals to verify so CI enforces them uniformly. Avoid per-module drift.

open as a page

As a tech lead, how would you design a fast, reliable Surefire test-execution strategy across a large multi-module build?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

Split fast unit tests (Surefire) from slow integration tests (Failsafe), parallelize units with forkCount per core while keeping per-fork resource isolation, pin one Surefire and test-framework version via parent POM, and enforce deterministic, thread-safe tests so the suite stays both fast and reliable.

open as a page