Code Coverage: Did I write enough tests?
Table of Content
Objective
Code coverage, also called test coverage, is a measure to what extent the source code of a program is executed when a test suite is run. It is often used to gauge the quality of the test suite, as such, to provide an assurance of software quality. There are a number of tools that can be used to measure code coverage. In this tutorial, we experiment two tools. JaCoCo, a Code Coverage library and tool for Java, and Visual Studio Code’s Coverage Tool. Thus, the objectives are three,
- To be able to use code coverage to guide the test case develoment;
- To be able to run JaCoCo and interpret the reports for Java projects;
- To be able to obtain code coverage using Visual Studio Code and interpret the reports for unmanaged Java projects.
Using JaCoCo for Unmanaged Projects
For small Java projects, it might not be necessary to use any IDE’s or to use any build tools, such as Maven or Gradle.
-
Download and unzip JaCoCo from https://www.eclemma.org/jacoco/. From the command line, If you have
curl
, you can download using:curl https://search.maven.org/remotecontent?filepath=org/jacoco/jacoco/0.8.13/jacoco-0.8.13.zip \ -o jacoco-0.8.13.zip
Suppose we want to unzip to the
lib
directory,mkdir -p lib cd lib unzip ../jacoco-0.8.13.zip
You will find a few jar files, e.g.,
$ ls jacoco-0.8.13/lib jacocoagent.jar jacocoant.jar jacococli.jar $
-
Assuming that we are writing Junit 5 test cases, we will need the Jar file for Junit 5. In the
lib
directory, download it usingcurl
or other methods of your choice,curl \ https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/1.13.0-M3/junit-platform-console-standalone-1.13.0-M3.jar \ -o junit-platform-console-standalone-1.13.0-M3.jar
-
Write a Java program, for instance, we create a simple Java class in the
src/main
directory,package main; public class Client { public void checkBool(boolean x, boolean y, boolean z) { if (x) { if (y && z) { showFlag(0); } else { showFlag(1); } } else { System.out.println("x is " + x); } } public void showFlag(int flag) { System.out.printf("Flag is %d", flag); } }
-
Write test case in the
src\test\TestClient.java
. We write one test case at a time:package test; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import main.Client; public class TestClient { private final PrintStream standardOut = System.out; private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream(); @BeforeEach public void setUp() { System.setOut(new PrintStream(outputStreamCaptor)); } @Test public void testXTrue() { Client cli = new Client(); cli.checkBool(true, false, false); Assertions.assertEquals("Flag is 1", outputStreamCaptor.toString().trim()); } @AfterEach public void tearDown() { System.setOut(standardOut); } }
-
Measure code coverage. This consists of compile the program and test code, generate coverage report, and examine the coverage report
javac -cp lib/junit-platform-console-standalone-1.13.0-M3.jar:bin \ -d bin src/main/*.java src/test/*.java mkdir report java -javaagent:lib/jacoco-0.8.12/lib/jacocoagent.jar=destfile=report/jacoco.exec ^ -jar lib/junit-platform-console-standalone-1.13.0-M3.jar ^ --class-path bin/ ^ --scan-class-path java -jar lib/jacoco-0.8.13/lib/jacococli.jar report report/jacoco.exec \ --classfiles bin \ --sourcefiles src \ --html report/html \ --xml report/report.xml \ --csv report/report.csv
In the above, the reports are generated in three formats,
html
,xml
, andcsv
. Let’s examine them, for instance, open thereport/html/index.html
file using a Web browser. -
Write more test case, and report the above step.
Using an IDE for Code Coverage
Modern IDE’s often integrates code coverage tool. For instance, JaCoCo
can be integrated with a number
of build tools, and can thus be integrated with the IDE’s that support these build tools. In this part,
we take a simple approach, i.e., to use Visual Studio Code’s test coverage support.
-
Download and install Visual Studio Code from https://code.visualstudio.com/download if you haven’t done so.
-
Set up Visual Studio Code for Java projects by following https://code.visualstudio.com/docs/java/java-project
-
Let’s begin with an unmanaged Java project. Create directories, e.g.,
mkdir myproject1 cd myproject1 mkdir -p lib bin src/main src/test
-
Download the Junit 5 Jar file to the
lib
directory as before. -
Create the
.vscode/settings.json
file, follow the instruction at https://code.visualstudio.com/docs/java/java-project, setjava.project.referencedLibraries
to reference the path of the Junit 5 Java file, e.g.,{ "java.project.sourcePaths": ["src"], "java.project.outputPath": "bin", "java.project.referencedLibraries": [ "lib/**/*.jar" ] }
The settings file also sets the source and output paths.
-
Write the program file, e.g.,
src/main/Client.java
-
Write the test file, e.g.,
src/test/TestClient.java
-
Follow the instruction https://code.visualstudio.com/docs/debugtest/testing#_test-coverage to compute and observe code coverage.