Normally when you run integration tests you have your test separated from the application under test and those tests use external interfaces exposed by that application. You also often have the application jar binary (a build) available in your artifact repository. But if it is possible to somehow estimate the code coverage that your integration tests assure. Yes it is. We’re take a look at the details with Jacoco code coverage tool.
What we need to have everything working
Basically you need to meet four prerequisites. Jacoco agent which will monitor and log which instructions are called in your application, Jacoco CLI tool that you will use to generate human readable report (both can be found in Maven central repository), jar file of your application under test and finally sources of the classes coverage of which you want to estimate (optional).
jacocoagent.jar
Jacocoagent.jar
is a Java agent that is to instrument the code of running application. So basically it introduces the logging instruction after each existing instruction of code under test.
jacococli.jar
It is Command Line Interface tool that can actually handle the entire instrumentation/reporting flow. However we’re going to use it in order to build HTML report after our tests have finished.
jar file of your application and/or dependencies
The whole process won’t move unless you have an application that you need to test. Hence you should have jar file that you will execute, perform testing, and then collect coverage data.
sources that will be used for explaining the coverage
If you have no sources, you will anyway get coverage report. But such report will not be detailed enough. You will get aggregated metrics for each class but you won’t be able to see coverage line by line of your sources.
Hence to have fully-featured report you need to supply sources for the classes you want to estimate coverage for.
Step-by-step guide
-
Execute application:
java -javaagent:<path_to_jacocoagent> -jar <path_to_jar>
-
Run your tests
-
Stop your application (you now have
jacoco.exec
file where you executed the app) -
Extract classes from your jar and probably inner jars or other jars attached as classpath (wherever classes you what to test the coverage for) so all they arranged under a single package structure (the latter is not necessary but will simplify everything)
Generating a report
Having all the above steps passed you can execute jacococli.jar
in order to generate coverage report (HTML-report in our case).
Linux case:
java -jar jacococli.jar report <path_to_jacoco_exec> \ --classfiles <path_to_root_of_classes_1> \ --classfiles <path_to_root_of_classes_2> \ --classfiles <path_to_root_of_classes_n> \ --html <path_to_folder_where_reports_to_store> \ --sourcefiles <path_to_sources_1> \ --sourcefiles <path_to_sources_1> \ --sourcefiles <path_to_sources_1>
Windows case:
java -jar jacococli.jar report <path_to_jacoco_exec>^ --classfiles <path_to_root_of_classes_1>^ --classfiles <path_to_root_of_classes_2>^ --classfiles <path_to_root_of_classes_n>^ --html <path_to_folder_where_reports_to_store>^ --sourcefiles <path_to_sources_1>^ --sourcefiles <path_to_sources_1>^ --sourcefiles <path_to_sources_1>
Example
Say you have jar application that packs the following structure:
. └── my-app.jar ├── BOOT-INF │ ├── classes │ │ ├── application.properties │ │ └── click │ │ └── webelement │ │ └── MyClass.class │ └── lib │ └── xxx ├── META-INF │ └── xxx └── org └── springframework └── xxx
This is a Spring-Boot application hence the jar is runnable. The part that we’re interested in is under BOOT-INF/classes
because there are the classes we’d like to have the coverage for.
For the sake of simplicity we assume that all jars from the example (jacocoagent.jar
, jacococli.jar
and my-app.jar
) are placed within the same folder and that folder is /tmp/coverage
.
Run your tests
So, switch to the folder you have all jars in (cd /tmp/coverage
) and run your application with Jacoco java agent: java -javaagent:/tmp/coverage/jacocoagent.jar -jar my-app.jar
. Perform all the integration tests you have planned. Terminate the application.
Now you should have
jacoco.exec
file under/tmp/coverage
.
Generate report
Alright. Now you need to extract the classes from your jar. Create some folder under /tmp/coverage
. For example your new folder would be /tmp/coverage/classes
. Extract BOOT-INF/classes
from your jar to /tmp/coverage/classes
so that the structure would be:
/tmp/coverage ├── classes │ └── click │ └── webelement │ └── MyClass.class ├── jacocoagent.jar ├── jacococli.jar ├── jacoco.exec └── my-app.jar
The last thing we need to do (in the case we want to have the color-coded sources in our report) is to supply the sources of extracted classes. For example you have the source project under /repos/my-app
and its structure looks like this:
/repos/my-app ├── src │ └── main │ ├── java │ │ └── click │ │ └── webelement │ │ └── MyClass.java │ └── resources │ └── lib │ └── xxx └── pom.xml
So that the command you need to run would be (I’m showing only Linux case here):
java -jar /tmp/coverage/jacococli.jar report /tmp/coverage/jacoco.exec \ --classfiles /tmp/coverage/classes \ --html /tmp/coverage/my-app-report \ --sourcefiles /repos/my-app/src/main/java
Well, this seems to be it. As the result you would get the html report that would be placed to the folder /tmp/coverage/my-app-report
(will be created if it does not exist).
As we supplied source code you will be able to drill the report down to the source code and watch which particular lines and branches were covered and which were not. If you still have the questions please send them to me using this form. I will amend the article according to your feedback.