Part 1 - Measuring coverage with Clover
Introduction
Part 1 of the Clover Tutorial focuses on the creation and interpretation of 'Current' Clover reports. Current reports display graphical and numerical data relating to the most recent coverage data collected for the project. This tutorial covers the initial creation of coverage data before stepping you through how to generate and interpret coverage reports. We'll the look at how to improve the coverage achieved by tests and regenerate the coverage reports. This section covers the very basic features of Clover and is an important first step for all users.
In this tutorial we will be compiling and unit-testing the Money library provided in the tutorial/src directory, then using Clover to determine how well the unit tests actually test the library.
In the first step, we will compile the Money library and run tests against it.
Compiling and running
In this step we will compile the library and run the tests against it without using Clover to check that everything is working correctly before including Clover in the next step. In the tutorial directory you will find the initial build file which contains targets for compiling, running and cleaning the build.
Compiling
To compile the java files use the command ant code.
Output should be similar to the following:
$ ant code Buildfile: build.xml code: [mkdir] Created dir: c:\clover\tutorial\build [javac] Compiling 4 source files to c:\clover\tutorial\build BUILD SUCCESSFUL Total time: 9 seconds
This shows that the java source files have been compiled and the class files have been placed in the c:\clover\tutorial\build directory.
Running the tests
To run the JUnit tests use the command ant test.
Output should be similar to the following:
$ ant test Buildfile: build.xml test: [java] ...................... [java] Time: 0.041 [java] OK (22 tests) BUILD SUCCESSFUL Total time: 3 seconds
This shows that all the tests have been run and have passed.
We have now compiled the Money library, and run tests against it. In the next step, we'll add Clover targets and properties to the build file to enable measurement of code coverage.
Adding Clover targets
Now that we've compiled the code and run unit tests, we are ready to add Clover targets and properties to the build file so we can measure the code coverage of the tests. Modifying the build file is trivial. Firstly we need to add a target to enable and configure Clover for the build.
Adding Clover task definitions
Load the build.xml file into your favourite text editor and add the Clover Ant task and type definitions:
<taskdef resource="clovertasks"/> <typedef resource="clovertypes"/>
These lines define the Clover Ant tasks which can then be used within the build file.
Adding a target to enable Clover
Add a target called with.clover which will enable and configure Clover for a build:
<target name="with.clover"> <clover-setup initString="demo_coverage.db"/> </target>
The initString value defines the location of the Clover coverage database. During compilation, Clover stores information about all the artifacts in your sourcebase to this file. If the database exists already, Clover updates it. If it doesn't exist, Clover will create a fresh database file. When instrumented code is run, Clover uses this database to initialise itself and then writes coverage recording files alongside the database file.
Adding Clover to the build classpath
The clover.jar needs to be in the runtime classpath when you execute the tests. To achieve this, add the line in bold below to the build.classpath Ant path:
<path id="build.classpath"> <pathelement path="${ant.home}/lib/clover.jar"/> <pathelement path="${ant.home}/lib/junit.jar"/> <pathelement path="${build}"/> </path>
Once you've made these changes, you can save the build.xml file. We will add some more Clover targets later to perform coverage reporting, but first we'll re-compile the Money library with Clover and re-run the tests to obtain coverage data.
Testing with Clover
We are now ready to measure the coverage of the tests over the Money library.
Compile with Clover
Ensure that your build has been cleaned by running ant clean. This deletes all class files from previous compilations.
Compile your code with Clover using the command ant with.clover code.
You will get output similar to the following:
$ ant with.clover code Buildfile: build.xml with.clover: compile: [mkdir] Created dir: C:\clover\tutorial\build [javac] Compiling 4 source files to C:\tutorial\build [clover] Clover Version 1.x, built on ... [clover] No coverage database 'C:\clover\tutorial\demo_coverage.db' found. Creating a fresh one. [clover] Clover all over. Instrumented 4 files.
The result of this process is that your source files have been instrumented by Clover and then compiled as usual.
Running the tests
We now need to run the tests again (with the command ant test). This will run the tests, this time measuring coverage. Output from Ant will be the same as a normal test run:
$ ant test Buildfile: build.xml run: [java] .................................. [java] Time: 0.08 [java] OK (22 tests) BUILD SUCCESSFUL Total time: 4 seconds
During this test run, Clover measured the code coverage of the tests and wrote the coverage data to disk. In the next step we'll generate a coverage report from this data to see how well the tests actually cover the Money library.
Creating a report
Adding a Clover report target
Open the build.xml file in a text editor and add the following target to create a HTML report:
The <current> element specifies that the type of report to be produced is a snapshot report of the current coverage data (historical reports, which show the progress of coverage over the life of the project, are discussed later in this tutorial (see Tutorial Part 2). The current report is to be in HTML format, written to the directory clover_html and with the title Clover demo. The output directory clover_html is relative to the path of the Ant build file. In this case, the directory clover_html will be nested within tutorial as this is the location of build.xml.
You can now view the report by opening the file tutorial\clover_html\index.html in a web browser. The next few sections of the tutorial will show you how to interpret the report and use it to improve the Money library unit tests.
Interpreting the report
We will now look at how to interpret the HTML report that you generated in the previous step.
The screenshot below shows the generated HTML report in a browser. In the top left hand corner is the list of packages. You can view all classes in the project or select a particular package to view. Clicking on the name of a package will bring up the relevant classes in the frame below it. Selecting one of these classes will bring up the source code in the frame on the right.
The header provides summary information relating to the current project. The left hand side displays the report title and the time of the coverage contained in the report. For current reports the timestamp is the timestamp of the most recent run of tests. The right hand side of the header displays metrics for the package, file or project overview which is currently selected. Depending on the current selection, the metrics include all or a subset of: Number of Lines of Code (LOC), Number of Non-commented Lines of Code (NCLOC), Number of Methods, Number of Classes, Number of Files and Number of Packages.
The screenshot shows the report for the Money.java source file with the green and red bar at the top showing the amount of code coverage on this class. The method, statement and conditional coverage percentages are beside this.
The left-most column shows line numbers and those that contain executable content are highlighted in blue. The second column shows the number of times a particular line has been executed during the test run. As you can see, lines 15-17 have been run 156 times by the JUnit tests, whereas line 28 has only been run twice.
If a line is never executed or has only been partially executed, the entire line of code will be highlighted in red. Depending on your browser, you can hover the mouse over a line to get a popup describing in detail the coverage information for that line. The following screenshot shows the coverage on a section of the MoneyBag.java source file:
Although line 52 of the above MoneyBag class has been executed 14 times, the method isZero() has never evaluated to true so it has not been fully tested. Therefore it, and the following two lines, are highlighted. This is also the case with lines 58 and 59.
This highlighting feature makes it easy for you to see which parts of the code have not been fully exercised by your tests so that you can then improve testing to provide better code coverage.
If any of the lines shaded red contained a bug, they may never be detected because the tests as they are don't test those parts of the code.
In the next step, we will enhance the JUnit tests to improve code coverage of the Money library.
Improving coverage
After having a look at the coverage report generated in the last step, you'll notice that coverage is not 100%. Although not always possible, it is best to get as close to full coverage as you can. Think of it this way: every line that isn't covered could contain a bug that will otherwise make it into production. You should certainly aim to cover all of the code that will be executed under normal operation of the software.
One method in the Money library that is not fully covered is the equals() method in the Money class (lines 40-42 as seen below). The first few lines of this method handle the special case when the Money value is zero. The coverage report shows that the code to handle this has not been covered by the tests. Line 40 has been executed 27 times but since it has never evaluated to true it has not been fully covered and is therefore in red. It follows then that the two successive lines have never been executed.
We can now improve the tests so that this section of code is covered. To do this, make the following additions (shown in bold) to the MoneyTest.java file.
Declare the variable f0USD:
public class MoneyTest extends TestCase { private Money f12CHF; private Money f14CHF; private Money f7USD; private Money f21USD; private Money f0USD; ...
Initialise f0USD in the setUp() method:
protected void setUp() { f12CHF = new Money(12, "CHF"); f14CHF = new Money(14, "CHF"); f7USD = new Money( 7, "USD"); f21USD = new Money(21, "USD"); f0USD = new Money(0, "USD"); ...
Finally, the following test needs to be added:
public void testMoneyEqualsZero() { assertTrue(!f0USD.equals(null)); IMoney equalMoney = new Money(0, "CHF"); assertTrue(f0USD.equals(equalMoney)); }