skip to content

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