JaCoCo
JaCoCo is a widely used tool for software developers. It discovers how much of the source code is executed by tests, providing insights into the effectiveness of the test cases we have written. Code coverage is important for ensuring critical parts of the code are tested and for enforcing the test coverage threshold in CI/CD pipelines.
JaCoCo with Gradle
For a Gradle project, we need to add this to build.gradle file
// 1. Apply the plugin
plugins{
id 'jacoco'
}
//2. Define exact version you want to target
jacoco {
toolVersion= "0.8.15"
}
We use gradle commands to generate the JaCoCo report.
./gradlew clean test
./gradlew jacocoTestReport
The test report files will be in build/reports/jacoco/test/html. The index.html is the root file, which can be opened using a browser to see the report in an HTML-formatted version.
Some Optimizations in the Gradle file
We can optimize JaCoCo to generate a report after tests are carried out, so we don’t need to run two commands. This can be done simply by adding “jacocoTestReport” task after “test”.
test {
useJUnitPlatform()
finalizeBy(jacocoTestReport) // Report is always generated after tests run
}
We can also fine-tune the ” jacocoTestReport ” task (by default generates only HTML):
jacocoTestReport {
dependsOn test
reports {
html.required.set(true) html.outputLocation.set(layout.buildDirectory.dir("reports/jacoco/test/html"))
xml.required.set(true)
xml.outputLocation.set(layout.buildDirectory.file("reports/jacoco/test/jacocoTestReport.xml"))
csv.required.set(false)
//csv.outputLocation.set(layout.buildDirectory.file("reports7jacoco/test/jacocoTestReport.csv"))
}
JaCoCo needs test report files to generate reports, so we added “dependsOn test” to ensure the task runs tests before executing the JaCoCo task.
Exclusions
We have some files that are never executed by the tests, or those lines of code are not important for testing. We exclude those files by defining exclusions in the “jacoco” task as follows:
jacocoTestReport {
...
...
afterEvaluate {
classDirecotries.setFrom(files(classDirectories.files.collect {
fileTree(dir:it, exclude: [
'**/com/kpaudel/Main.class',
'**/com/kpaudel/Main$*.class', //for inner/anonymous classes
'**/config/**',
'**/dto/**',
'**/entity/**'])
}))
}
}
SonarQube
Let’s move to SonarQube. Actually, SonarQube is also a very well-known tool. It can be used for two purposes:
In Development: Automatically scans source code for syntax errors, logic bugs, code smells, vulnerabilities, and duplications.
In Deployment: Connects to automation tools such as Jenkins, GitHub Actions, and GitLab CI to block bad code merges via Quality Gates.
SonarQube provides visual dashboards to view the problems and proper fixes with explanations. It supports the vast majority of programming languages.
Installation
To use Sonar, you need to own a server or use a cloud-managed service. Because of its flexibility and security, and because it can be used on-premises, I prefer to set up your own SonarQube Server. The following minimalistic docker-compose.xml can create a SonarQube container in just one command.
version: '3.8'
services:
sonarqube:
image: sonarqube:latest
container_name: sonarqube
ports:
- "8050:9000"
environment:
- SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true
volumes:
- ./volume/sonarqube_data:/opt/sonarqube/data
- ./volume/sonarqube_extensions:/opt/sonarqube/extensions
- ./volume/sonarqube_logs:/opt/sonarqube/logs
restart: unless-stopped
The default credentials are admin/admin, which can be changed later after logging in. So, after installation, we have the SonarQube server, which can be used for code analysis and CI/CD pipelines.
SonarQube with Gradle
So far, so good; now, let’s make some changes in our build.gradle file in the project.
plugins {
id 'java'
id 'jacoco'
id 'org.sonarqube' version '7.4.0.8496'
}
sonar {
properties {
property "sonar.projectKey", "test-automation"
property "sonar.projectName", "Test Automation"
property "sonar.host.url", "${sonarURL}"
property "sonar.token", "${sonarToken}"
property "sonar.coverage.jacoco.xmlReportPaths", layout.buildDirectory.file("reports/jacoco/test/jacocoTestReport.xml").get().asFile.path
}
}
Here, I have saved the sonarURL and sonarToken in ~/.gradle/gradle.properties file. The tokens can be created from the server by selecting your account → Security.
So, we are now ready to run Sonar so that the analysis report will be visible for the project on the server.
Connect SonarQube from IDE
We can comfortably connect to the SonarQube Server from the “SonarQube for IDE” plugin. You can create a new connection and provide Server Details (like the server URL and token)