Code coverage is a measure of how many lines of our code are covered by the Unit Test. This is very helpful, so we can evaluate the quality of our code, how much of our code has been and has not been covered by the Unit Test. In this tutorial, I will introduce to you all an Apache Maven plugin called JaCoCo Maven Plugin that helps us to measure the code coverage.
I will create a new Maven project with a simple example with Unit Test as follows:
JUnit dependency is as follows:
1 2 3 4 5 6 |
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.11.2</version> <scope>test</scope> </dependency> |
Calculation class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.huongdanjava.mavenjacocoplugin; public class Calculation { public int add(int a, int b) { return a + b; } public int sub(int a, int b) { if (a >= b) { return a - b; } return b - a; } } |
CalculationTest class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package com.huongdanjava.mavenjacocoplugin; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class CalculationTest { private static Calculation calculation; @BeforeAll public static void init() { calculation = new Calculation(); } @Test public void testAdd() { assertEquals(4, calculation.add(1, 3)); } @Test public void testSub() { assertEquals(2, calculation.sub(7, 5)); assertEquals(9, calculation.sub(3, 12)); } } |
JaCoCo Maven Plugin:
1 2 3 4 5 |
<plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.12</version> </plugin> |
OK, let’s get started.
JaCoCo Maven Plugin defines a lot of goals, but there is a goal that we need to declare so that the plugin can calculate the code coverage for us, that’s goal: prepare-agent. Suppose, I now declare this goal in the example project above:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.12</version> <executions> <execution> <id>agent-for-ut</id> <goals> <goal>prepare-agent</goal> </goals> </execution> </executions> </plugin> |
then right-click on the project, select Run As, and select Maven test, then you’ll see the results in the Eclipse Console tab as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
/Users/khanhnguyenj/.sdkman/candidates/java/21.0.4-tem/bin/java -Dmaven.multiModuleProjectDirectory=/Users/khanhnguyenj/Documents/code/huongdanjava.com/maven-jacoco-plugin -Djansi.passthrough=true -Dmaven.home=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3 -Dclassworlds.conf=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/bin/m2.conf -Dmaven.ext.class.path=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven-event-listener.jar -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=50095:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath /Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/boot/plexus-classworlds.license:/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven3/boot/plexus-classworlds-2.8.0.jar org.codehaus.classworlds.Launcher -Didea.version=2024.2.3 test [INFO] Scanning for projects... [INFO] [INFO] ----------------< com.huongdanjava:maven-jacoco-plugin >---------------- [INFO] Building maven-jacoco-plugin 0.0.1-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- jacoco:0.8.12:prepare-agent (agent-for-ut) @ maven-jacoco-plugin --- [INFO] argLine set to -javaagent:/Users/khanhnguyenj/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/Users/khanhnguyenj/Documents/code/huongdanjava.com/maven-jacoco-plugin/target/jacoco.exec [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ maven-jacoco-plugin --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /Users/khanhnguyenj/Documents/code/huongdanjava.com/maven-jacoco-plugin/src/main/resources [INFO] [INFO] --- compiler:3.13.0:compile (default-compile) @ maven-jacoco-plugin --- [INFO] Recompiling the module because of added or removed source files. [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent! [INFO] Compiling 1 source file with javac [debug target 21] to target/classes [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ maven-jacoco-plugin --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /Users/khanhnguyenj/Documents/code/huongdanjava.com/maven-jacoco-plugin/src/test/resources [INFO] [INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ maven-jacoco-plugin --- [INFO] Recompiling the module because of changed dependency. [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent! [INFO] Compiling 1 source file with javac [debug target 21] to target/test-classes [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ maven-jacoco-plugin --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.huongdanjava.mavenjacocoplugin.CalculationTest [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.021 s -- in com.huongdanjava.mavenjacocoplugin.CalculationTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.041 s [INFO] Finished at: 2024-10-14T07:42:12+08:00 [INFO] ------------------------------------------------------------------------ Process finished with exit code 0 |
As you can see in the line:
1 2 |
[INFO] --- jacoco:0.8.12:prepare-agent (agent-for-ut) @ maven-jacoco-plugin --- [INFO] argLine set to -javaagent:/Users/khanhnguyenj/.m2/repository/org/jacoco/org.jacoco.agent/0.8.12/org.jacoco.agent-0.8.12-runtime.jar=destfile=/Users/khanhnguyenj/Documents/code/huongdanjava.com/maven-jacoco-plugin/target/jacoco.exec |
a file named jacoco.exec was created in the target directory of the project. This is the file containing the code coverage report that JaCoCo Maven Plugin created. This file is in binary format so we can not see it directly, but we still have another way to view this report result using the goal report of JaCoCo Maven Plugin.
Let’s run the Maven project with the goal jacoco:report to see how the results:
1 |
mvn clean test jacoco:report |
At this point, if you check the target directory for the project, you will see a directory named site containing the code coverage information that JaCoCo Maven Plugin created after we ran the project with the report goal:
Open the index.html file in the target/site/jacoco directory and you will see the following output:
This is all the information about code coverage in our project. Can you easily see how much code coverage for each package in our project?
You can click on each package to see details:
As you can see, we can see code coverage details for each method. You can also click on each method to see what code we have covered.
By default, the code that is covered will be highlighted in green, and vice versa will be red. So if I now modify the code of the CalculationTest, remove:
1 |
assertEquals(9, calculation.sub(3, 12)); |
in the testSub() method, you will see the code coverage report for class Calculation now as follows: