Test coverage with JaCoCo
Coverage shows which lines and branches of your code were executed while the tests ran. It answers the question "what did my tests not touch at all?". For Java the standard tool is JaCoCo: it attaches to the test JVM, records executed code and builds a report.
Adding JaCoCo
Add the plugin to build/plugins in pom.xml:
<!-- pom.xml -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.15</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>prepare-agent puts JaCoCo's Java agent into the argLine property before the tests. Surefire picks it up through @{argLine} in its configuration — this is why the Mockito lesson used @{argLine} and not a plain value. report builds the report right after the tests.
Running
mvn testAfter the usual test output Maven prints:
[INFO] Tests run: 60, Failures: 0, Errors: 0, Skipped: 2
[INFO]
[INFO] --- jacoco:0.8.15:report (report) @ blog ---
[INFO] Loading execution data file ...\target\jacoco.exec
[INFO] Analyzed bundle 'blog' with 7 classes
[INFO] BUILD SUCCESSOpen target/site/jacoco/index.html in a browser. The report has a table by package and class; clicking a class shows its source code with colored lines:
| Color | Meaning |
|---|---|
| Green | The line was executed |
| Yellow | Only some branches of a condition were executed |
| Red | The line was never executed |
What the report showed
For the course project the report found two gaps. In InMemoryPostRepository the deleteById method is red — no test calls it. In PriceCalculator the line if (percent < 0 || percent > 100) is yellow: there is a test for 120%, but none for a negative discount. Both are honest hints: write a test for deleting and one for -5.
The same numbers are in target/site/jacoco/jacoco.csv:
GROUP,PACKAGE,CLASS,INSTRUCTION_MISSED,INSTRUCTION_COVERED,BRANCH_MISSED,BRANCH_COVERED,LINE_MISSED,LINE_COVERED,...
blog,com.example.blog,InMemoryPostRepository,7,58,0,2,1,10,...
blog,com.example.blog,PostService,0,73,0,4,0,16,...
blog,com.example.shop,PriceCalculator,0,46,1,5,0,9,...How much is enough
Coverage shows what was not tested, but says nothing about the quality of what was. A test without assertions executes all lines and gives 100% while checking nothing. Treat the report as a map of blind spots, not as a score. Many teams aim for 70–80% on business logic and do not test trivial code such as getters for the sake of the number.
Common mistake
If Surefire's argLine is written without @{argLine}, it overwrites the value JaCoCo prepared. The tests pass, and the report silently disappears:
<argLine>-javaagent:${org.mockito:mockito-core:jar} -Xshare:off</argLine>[INFO] --- jacoco:0.8.15:report (report) @ blog ---
[INFO] Skipping JaCoCo execution due to missing execution data file.Start the value with @{argLine}: <argLine>@{argLine} -javaagent:...</argLine>. Surefire substitutes the JaCoCo agent there at run time.