This is a "multi-multi" tutorial showing how to use Clover:
If you have questions like:
then this tutorial is for you
Confused which scenario you shall use? Have a quick look at the Decision Matrix.
For the simplicity of the tutorial it's assumed that:
In case when your environment is different and:
TIP: this scenario applies also to a case when
1) because of fact that
there is no need to configure a distributed coverage feature.
2) because of fact that
we have the same code base, so the same clover.db should be used on each application server and merging clover databases has no sense
we should keep coverage recordings from all servers in the same location as well - note that they will not overwrite each other, because every snapshot file has unique file name
3) because of fact that
it will be very convenient to use it to store clover.db as well as recordings
we will not need to use -Dclover.initstring at runtime, because the absolute path will be hardcoded in the instrumented code
1) Build application with Clover
a) using Ant
Define initstring attribute for <clover-setup> or <clover-instr> tag, e.g.:
<clover-setup initstring="/path/to/network/drive/clover.db">
b) using Maven
Define <cloverDatabase> property for Clover plugin in pom.xml, e.g.:
<plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-clover2-plugin</artifactId> <configuration> <cloverDatabase>/path/to/network/drive/clover.db</cloverDatabase> </configuration> </plugin>
mvn clean clover2:setup install
2) Deploy instrumented application to Application Servers
Copy clover.jar and your application jar/war. There's no need to copy clover.db as it's on a network drive.
3) Run tests on Application Servers
Execute your application. There is no need to provide clover.initstring parameter as path to clover.db database is already hardcoded in instrumented sources.
4) Generate coverage report
a) using Ant
Execute <clover-report> task; use initstring pointing to clover.db on a network drive, e.g.:
<clover-report initstring="/path/to/network/drive/clover.db">
b) using Maven
Execute clover2:clover goal. Note that you don't need to call clover2:aggregate as the project is not multi-module. The <cloverDatabase> defined in pom.xml will be used for reporting.
<plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-clover2-plugin</artifactId> <configuration> <cloverDatabase>/path/to/network/drive/clover.db</cloverDatabase> </configuration> </plugin>
mvn clover2:clover
In this scenario there are two approaches possible:
Approach #1: use one database for all modules
Approach #2: use separate database for every module but under common root
We will focus on Approach #1 as it's easier to manage. For Approach #2 - see chapter below.
1) because of fact that
there is no need to configure a distributed coverage feature.
2) because of fact that
we have the same code base, so one clover.db should be used on each application server and merging of clover databases has no sense
we should keep coverage recordings from all servers in the same location as well - note that they will not overwrite each other, because every snapshot file has unique file name
3) because of fact that
we will not need to use -Dclover.initstring at runtime, because the absolute path will be hardcoded in the instrumented code
1) Build application with Clover
a) using Ant
<target name="all"> <!-- Enable clover for top level module --> <clover-setup initstring="/path/to/network/drive/clover.db"> <!-- Build sub-modules ensuring that properties are passed --> <ant inheritrefs="true" inheritprops="true" file="sub-module-a/build.xml" target="all"/> <ant inheritrefs="true" inheritprops="true" file="sub-module-b/build.xml" target="all"/> </target>
b) using Maven
<!-- TOP LEVEL POM.XML --> <plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-clover2-plugin</artifactId> <configuration> <cloverDatabase>/path/to/network/drive/clover.db</cloverDatabase> <singleCloverDatabase>true</singleCloverDatabase> </configuration> </plugin> <!-- CHILD MODULES POM.XML --> <!-- No need to define anything for Clover, unless you wish to have some module-specific settings -->
2) Deploy instrumented application to Application Servers
Copy clover.jar and your application jar/war. There's no need to copy clover.db as it's on a network drive.
3) Run tests on Application Servers
Execute your application. As we have used singleCloverDatabase and cloverDatabase pointing to absolute path on a network drive, we don't need to provide clover.initstring parameter.
4) Generate coverage report
a) using Ant
<clover-report initstring="/path/to/network/drive/clover.db">
b) using Maven
Execute clover2:clover goal. Note that you don't need to call clover2:aggregate as the project is not multi-module. The <cloverDatabase> defined in top-level pom.xml will be used for reporting.
mvn clover2:clover
This scenario is the same as Scenario 2, but we use separate clover.db database for every module and all of them are stored under common root.
Steps:
1) Build application with Clover
a) using Ant
This approach is generally not applicable for ant scripts.
b) using Maven
Don't define <cloverDatabase> and <singleCloverDatabase> so that default values will be used (relative path target/clover/clover.db and false).
2) Deploy instrumented application to Application Servers
No differences.
3) Run tests on Application Servers
Provide clover.initstring.basedir=/path/to/top-level-module/dir at runtime. Alternatively: use clover.initstring.prefix.
4) Generate coverage report
Before generating report you have to merge all databases. For example:
a) using Ant
<clover-merge initstring="/path/to/mergedClover.db"> <cloverDb initstring="/path/to/network/drive/moduleA/clover.db"/> <cloverDb initstring="/path/to/network/drive/moduleB/clover.db"/> </clover-merge> <clover-report initstring="/path/to/mergedClover.db"/>
b) using Maven
<!-- Top-level pom.xml --> <plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-clover2-plugin</artifactId> <configuration> <cloverMergeDatabase>/path/to/network/drive/cloverMerged.db</cloverMergeDatabase> </configuration> </plugin>
mvn clover2:aggregate clover2:clover
1) because of fact that
there is no need to configure a distributed coverage feature.
2) because of fact that
it implies that
3) because of fact that
we will not need to use -Dclover.initstring at runtime, because the absolute path will be hardcoded in the instrumented code
1) Build application with Clover
a) using Ant
Define initstring attribute for <clover-setup> or <clover-instr> tag, which will point to different directory for every application ,e.g.:
<!-- App1 build.xml --> <clover-setup initstring="/path/to/network/drive/app1/clover.db"> <!-- App2 build.xml --> <clover-setup initstring="/path/to/network/drive/app2/clover.db">
b) using Maven
Define <cloverDatabase> property for Clover plugin in pom.xml. You can use singleCloverDatabase in case your application is multi-module. Databases for all applications must be stored under common root (it's a limitation of clover2:merge goal; Ant clover-merge task is more flexible regarding paths). Example:
<!-- App1 pom.xml --> <plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-clover2-plugin</artifactId> <configuration> <cloverDatabase>/path/to/network/drive/common-root/app1/clover.db</cloverDatabase> </configuration> </plugin> <!-- App2 pom.xml --> <plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-clover2-plugin</artifactId> <configuration> <cloverDatabase>/path/to/network/drive/common-root/app2/clover.db</cloverDatabase> <singleCloverDatabase>true</singleCloverDatabase> <!-- assuming that app2 is multi-module --> </configuration> </plugin>
mvn clean clover2:setup install
2) Deploy instrumented application to Application Servers
Copy clover.jar and your application jar/war to proper machines. There's no need to copy clover.db as it's on a network drive.
3) Run tests on Application Servers
Execute your applications. As every application runs in their own JVM and due to fact that we have used cloverDatabase (and optionally singleCloverDatabase) pointing to absolute path on a network drive, we don't need to provide clover.initstring parameter at runtime, because correct path is hardcoded in instrumented classes.
4) Generate coverage report
Before generating report you have to merge all databases. for example:
a) using Ant
<clover-merge initstring="/path/to/network/drive/cloverMerged.db"> <cloverDb initstring="/path/to/network/drive/app1/clover.db"/> <cloverDb initstring="/path/to/network/drive/app2/clover.db"/> </clover-merge> <clover-report initstring="/path/to/cloverMerged.db"/>
b) using Maven
<!-- Top-level pom.xml --> <plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-clover2-plugin</artifactId> <configuration> <cloverMergeDatabase>/path/to/network/drive/cloverMerged.db</cloverMergeDatabase> <!-- output database --> <baseDir>/path/to/network/drive/common-root</baseDir> <!-- common root --> <includes>*.db</includes> <!-- filename pattern, separated by comma or space --> </configuration> </plugin>
mvn clover2:aggregate # run it for multi-module applications mvn clover2:merge clover2:clover # run it for final report
This scenario is a variation of Scenario 3 in such way, that:
In such case, you have to:
app1/clover.db app2/clover.db app2/moduleA/clover.db app2/moduleB/clover.db
/path/to/common-root/app1/clover.db /path/to/common-root/app2/clover.db /path/to/common-root/app2/moduleA/clover.db /path/to/common-root/app2/moduleB/clover.db
1) because of fact that
it implies that
2) because of fact that
we don't have to merge clover databases as the same database should be used on each application server
3) because of fact that
we will not need to use clover.initstring at runtime, because the absolute path will be hardcoded in the instrumented code
1) Build application with Clover
a number greater than 0 means that server will hold until all clients are connected before it continues execution; number equal 0 means that tests will start immediately
you might have a dependency loop so that server waits for clients and clients wait for server - see below
a) using Ant
<clover-setup initstring="/path/to/network/drive/clover.db"> <distributedCoverage host="my.server.com" port="1234" numClients="2" timeout="10000"/> </clover-setup>
b) using Maven
<plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-clover2-plugin</artifactId> <configuration> <cloverDatabase>/path/to/network/drive/clover.db</cloverDatabase> <singleCloverDatabase>true</singleCloverDatabase> <!-- In case of multi-module application (optional) --> <distributedCoverage> <host>my.server.com</host> <port>1234</port> <numClients>2</numClients> <timeout>10000</timeout> </distributedCoverage> </configuration> </plugin>
2) Deploy instrumented application to Application Servers
Copy clover.jar and your application jar/war to proper machines. There's no need to copy clover.db as it's on a network drive.
3) Run tests on Application Servers
On <<server>> machine
java ...
-Dclover.server=
true
On <<client>> machines
You don't have to provide any runtime options for JVM. They're already compiled in the code.
Potential problems
Server does not wait for clients, despite having numClients != 0 in build configuration
Do not use -Dclover.distributed.coverage=ON
runtime option if numClients!=0 was set in instrumentation. The clover.distributed.coverage provided at runtime will override numClients setting from instrumentation, setting it to 0. As a consequence your tests on server will start immediately, without waiting for clients to connect. It can result in lower or zero coverage.
Instead of this:
Execution of tests hangs when with numClients != 0
It can happen that your server will wait for clients to connect, while clients will wait until server starts unit test execution. This is a typical case for web applications running in container (like Tomcat, JBoss), when your unit test calls a servlet class (e.g. via HTTP request). The issue is as follows:
See Working with Distributed Applications how to fix this circular dependency.
4) Generate coverage report
a) using Ant
<clover-report initstring="/path/to/network/drive/clover.db"> <current showUniqueCoverage="true" outfile="/path/to/clover/report"> <format type="html"/> <fileset dir="src"/> </current> </clover-report>
b) using Maven
In order to show per-test coverage in the HTML report (showUniqueCoverage), you have to use the custom <reportDescriptor> in pom.xml and in the report descriptor set the showUniqueCoverage=true. For example:
<project name="Clover Report" default="current"> <clover-setup initString="${cloverdb}"/> <target name="historical"/> <target name="current"> <clover-report> <current showUniqueCoverage="true" outfile="${output}"> <!-- Show per-test coverage in report --> <format type="html"/> </current> </clover-report> </target> </project>
<plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-clover2-plugin</artifactId> <configuration> <cloverDatabase>/path/to/network/drive/clover.db</cloverDatabase> <outputDirectory>/path/to/clover/report</outputDirectory> <reportDescriptor>report-descriptor.xml</reportDescriptor> <!-- Use custom report --> </configuration> </plugin>
mvn clover2:clover
More information about format of report descriptor can be found here:
1) because of fact that we're not interested in per-test coverage or test optimization, there is no need to configure a distributed coverage feature
2) because of fact that we have one multi-module application and we want to have a single report showing combined coverage from all modules, one clover.db should be used and there's no need to merge clover databases
3) because of fact that we will generate reports with history,
4) because of fact that
we will not need to use clover.initstring at runtime, because the absolute path will be hard-coded in the instrumented code.
1) Build application with Clover
Configuration is the same as for Scenario 2.
a) using Ant
<target name="all"> <!-- Enable clover for top level module --> <clover-setup initstring="/path/to/network/drive/clover.db"> <!-- Build sub-modules ensuring that properties are passed --> <ant inheritrefs="true" inheritprops="true" file="sub-module-a/build.xml" target="all"/> <ant inheritrefs="true" inheritprops="true" file="sub-module-b/build.xml" target="all"/> </target>
b) using Maven
<!-- TOP LEVEL POM.XML --> <plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-clover2-plugin</artifactId> <configuration> <cloverDatabase>/path/to/network/drive/clover.db</cloverDatabase> <singleCloverDatabase>true</singleCloverDatabase> </configuration> </plugin> <!-- CHILD MODULES POM.XML --> <!-- No need to define anything for Clover, unless you wish to have some module-specific settings -->
mvn clover2:setup test
2) Deploy instrumented application to Application Servers
Remove previous version of application and copy clover.jar and your application jar/war. There's no need to copy clover.db as it's on a network drive.
3) Run tests on Application Servers
Execute your application. As we have used singleCloverDatabase and cloverDatabase pointing to absolute path on a network drive, we don't need to provide clover.initstring parameter at runtime.
4) Generate coverage report
a) using Ant
<clover-report initstring="/path/to/network/drive/clover.db"> <current outfile="/path/to/clover/report/current" title="Coverage Report"> <format type="html"/> <fileset dir="src"/> </current> <historical outfile="/path/to/clover/report/historical" title="Historical Report" historyDir="/path/to/clover/historypoints"> <format type="html"/> </historical> </clover-report> <clover-historypoint historyDir="/path/to/clover/historypoints"> <fileset dir="src"/> </clover-historypoint>
b) using Maven
<!-- Top-level pom.xml --> <plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-clover2-plugin</artifactId> <configuration> <cloverDatabase>/path/to/network/drive/clover.db</cloverDatabase> <singleCloverDatabase>true</singleCloverDatabase> <generateHistorical>true</generateHistorical> <generateHtml>true</generateHtml> <historyDir>/path/to/clover/historypoints</historyDir> <outputDirectory>/path/to/clover/report</outputDirectory> </configuration> </plugin>
mvn clover2:clover clover2:save-history
Q1 | How many applications do you build? (term 'application' means a separate source code and independent build) | |||
one application | many applications | |||
Q2 | How many modules application(s) has(have)? (term 'module' means a part of source code, built in the same session as other parts of code) | |||
one module | many modules | one module | many modules | |
Solution | ||||
Recommended | define cloverDatabase in pom.xml no need to merge | define cloverDatabase in master pom.xml set singleCloverDatabase=true in master pom.xml no need to merge | define cloverDatabase in pom.xml all applications' databases must be stored under a common root merge databases from all applications after tests | define cloverDatabase in master pom.xml set singleCloverDatabase=true in master pom.xml all applications' databases must be stored under a common root merge databases from all applications after tests |
Alternative #1 | n/a | set singleCloverDatabase=false (or don't define) merge after tests via clover:aggregate | n/a | set singleCloverDatabase=false (or don't define) all applications' databases must be stored under a common root |
Q3 | Is directory with clover database(s) accessible at the same absolute same path on build and test server? | |||||||
yes | no | |||||||
Q4 | Do you have many applications (i.e. you have many clover databases generated)? | |||||||
one application | many applications | one application | many applications | |||||
Q5 | Do you run each application in a separate JVM? | |||||||
n/a | separately | together | n/a | separately | together | |||
Solution | ||||||||
Recommended | nothing to do; path to clover.db is already hardcoded in instrumented source code | instrument source code with different initstring for every app; no need to provide clover.initstring at runtime as it's hardcoded in instrumented source code | instrument code with relative path in initstring for all applications; provide common root in clover.initstring.basedir at runtime | provide clover.initstring=/path/to/clover.db at runtime | instrument source code with for every application at runtime | instrument code with relative path |
Q6 | In case you use different machines for build/test/reporting - do you have a shared network drive? | |||
---|---|---|---|---|
yes | no | |||
Q7 | Do you execute the same application (i.e. binaries produced in one build and using the same clover.db) on several machines? | |||
no | yes | no | yes | |
Solution | ||||
nothing to do (clover.db created during build is available on test machine too thanks to a network drive; coverage recording files are written to the same directory) | nothing to do (clover.db created during build is accessible on all test machines too; coverage recordings from all test machines are written to the same directory; coverage files generated on different machines will not clash because they're using unique file names) | copy clover.db from build to test server execute tests copy clover.db and coverage files from test to report server | copy clover.db from build to test servers execute tests copy clover.db from build to report server |
Q8 | Are you interested in per-test coverage report or in test optimization? | |||
---|---|---|---|---|
no | yes | |||
Q9 | Do you have distributed application, so that single test case executes application logic on several machines? | |||
no | yes | no | yes | |
Solution | ||||
nothing to do | don't set up distributed coverage feature just run your application and gather | don't set up distributed coverage feature use showUniqueCoverage=true for reporting | instrument code with distributedCoverage option at runtime, designate one server where unit tests are use showUniqueCoverage=true for reporting |