Test Automation using JaCoCo & SonarQube

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)

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *