Introduction

In this post we will see how to work with Junit code coverage with JaCoCo, which is quite a new maven plug-in that provides the JaCoCo runtime agent to your tests and allows basic report creation. Currently it supports instruction, branch, line, method and class coverage which is pretty enough you can expect from this kind of tool. Additionally, it can measure and report cyclomatic complexity for methods and summarize the complexity for classes and packages.

So if you want to have line number information included in the coverage reports or you want source code highlighting the class files of the test target must be compiled with debug information.

Note when it is used in conjuction with the maven-surefire-plugin or maven-failsafe-plugin you must not use a forkCount of 0 or set the forkMode to never as this would prevent the execution of the tests with the javaagent set and no coverage would be recorded.

What is branch coverage?

Decision Coverage is also known as Branch Coverage or all-edges coverage. It covers both the true and false conditions unlikely the statement coverage. A branch is the outcome of a decision, so branch coverage simply measures which decision outcomes have been tested. For example, with the loop control statement like while or for or if statement the outcome is either true or false and the decision coverage ensures that each outcome(i.e true and false) of control statement has been executed at least once.

Example

Creating Project

Create a standalone maven project with artifact-id junit-jacoco and group-id com.jeejava in Eclipse.

Updating Build File

Modify the pom.xml file to include the required dependencies as shown below.

Notice we have included junit as a dependency and jacoco as a plugin into the below maven build file.

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.jeejava</groupId>
	<artifactId>junit-jacoco</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
		<junit.version>4.12</junit.version>
		<jacoco.version>0.8.4</jacoco.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
			<!-- JaCoCo plugin -->
			<plugin>
				<groupId>org.jacoco</groupId>
				<artifactId>jacoco-maven-plugin</artifactId>
				<version>${jacoco.version}</version>
				<executions>
					<execution>
						<goals>
							<goal>prepare-agent</goal>
						</goals>
					</execution>
					<execution>
						<id>report</id>
						<phase>prepare-package</phase>
						<goals>
							<goal>report</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

</project>

Creating Class

Create a Calculation class with the below source code under src/main/java directory. This class has to be tested later using Junit class and test report will be generated using Jacoco plugin.

package com.jeejava.junit.jacoco;

public class Calculation {

	public int findMax(int arr[]) {
		int max = arr[0];
		for (int i = 1; i < arr.length; i++) {
			if (max < arr[i])
				max = arr[i];
		}
		return max;
	}

	public int getRemainder(int dividend, int divisor) {
		if (divisor <= 0) {
			throw new IllegalArgumentException("Divisor should be greater than 0");
		}
		return dividend % divisor;
	}

}

Creating Junit Class

Create a JUnit CalculationTest class with the below source code under src/test/java directory.

package com.jeejava.junit.jacoco;

import static org.junit.Assert.assertEquals;

import org.junit.Before;
import org.junit.Test;

public class CalculationTest {

	private Calculation calc;

	@Before
	public void setup() {
		calc = new Calculation();
	}

	@Test
	public void testFindMax() {
		int result = calc.findMax(new int[] { 1, 3, 4, 2 });
		assertEquals(4, result);
		result = calc.findMax(new int[] { -12, -1, -3, -4, -2 });
		assertEquals(-1, result);
	}

	@Test
	public void testGetRemainder() {
		int result = calc.getRemainder(5, 2);
		assertEquals(1, result);
	}

}

Testing and Generating Test Report

Testing as Junit Test

Now run the above test class in Eclipse, you should see the below output.

junit code coverage

Generating Test Report

JaCoCo plugin will trigger the measurement of code coverage every time unit tests are run using mvn test. The results will be saved by default into target/jacoco.exec binary file.

But the problem is, reading the binary file manually is almost impossible so it is better to convert it to a more user-friendly version using command mvn jacoco:report.

Now you will find the report has been generated in html, csv and xml formats under target directory in site/jacoco.

If you do double-click on the index.html file, you will below output (for details output you can go further inside the com.jeejava.junit.jacoco directory). Here you will find the missed coverage.

junit code coverage

Now click on com.jeejava.junit.jacoco under header Element. Here you will also find the method for which missed coverage occurred.

junit jacoco

Now click on any one of the Element. Here you will find the more details of the code coverage using test cases.

For example, com.jeejava.junit.jacoco -> Calculation.java -> getReminder() will produce below output:

The green lines represent the full coverage with the parts of the code. The yellow line represents missing coverage because the line was never evaluated by the code. And the red line tells the line was never executed by the test cases.

Therefore we will bridge the gap of the test coverage by changing the testGetRemainder() method as shown below in the code snippets:

@Test(expected = IllegalArgumentException.class)
public void testGetRemainder() {
	int result = calc.getRemainder(5, 2);
	assertEquals(1, result);
	calc.getRemainder(5, 0);
}

In the above method I have put expected=IllegalArgumentException because I know this exception is expected and I do not want the method throws the expected exception to mark test case failure.

Now again execute commands mvn test and mvn jacoco:report and check the html report again.

Source Code

You can download source code.

Thanks for reading.

Tags:

Leave a Reply

Your email address will not be published. Required fields are marked *