Step-by-Step Guide to Code Coverage with JaCoCo and SonarQube

Introduction
Developers write unit test cases to validate the functions, classes, or group of code that represents the functionality(business logic) by breaking system into small functional units to test the behavior against the expected outcomes. Unit test cases ensure the seamless execution of business logic by effectively managing success/failure conditions and error handling.
Code coverage is a critical metric to gauge the effectiveness of code testing. It represents the percentage of the codebase is covered by written test cases and provides the insights into potential areas where bugs and vulnerabilities might go undetected.
There are a number of Code coverage tools are available for various languages. In this article, JaCoCO plugin used to generate detailed code coverage reports as a part of the build process. We will configure the SonarScanner analysis to import coverage reports into SonarQube for better visualization of the metrics.
Now, let’s walk through how to implement the integration of the Java code test coverage report to SonarQube.
Install Java:
sudo apt update -y
wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | tee /etc/apt/keyrings/adoptium.asc
echo "deb [signed-by=/etc/apt/keyrings/adoptium.asc] https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list
sudo apt update -y
sudo apt-get install temurin-21-jdk
/usr/bin/java --version
Docker installation:
sudo apt-get update
sudo apt-get install docker.io -y
sudo usermod -aG docker $USER
newgrp docker
sudo chmod 777 /var/run/docker.sock
Create a SonarQube container using community Docker image. Access the application at http://localhost:9000 with the default login credentials: admin/admin.
docker run -d --name sonar -p 9000:9000 sonarqube:lts-community
Generate authentication token to push coverage reports.
SonarQube web url http://localhost:9000 —>Click on Administration → Security → Users → Click on Tokens and Update Token → <Give a token name> → click on Generate Token
Configure Sonar properties:
Properties for SonarScanner and Jacoco can either be configured in the setings.xml file or passed directly as options in the command-line interface (CLI). In this example, we choose to pass properties as options in the CLI.
Quality gates contain conditions that will cause a code scan to fail if specified specs are not met. It is an industry practice to maintain at least 80% code coverage to promote a build to higher environments.
Install SonarScanner:
The SonarScanner is a command line tool to execute SonarQube tests.
sudo wget [https://binaries.sonarsource.com/?prefix=Distribution/sonar-scanner-cli/sonar-scanner-cli-7.0.2.4839.zip](https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.0.2.4839.zip)
unzip sonar-scanner-cli-7.0.2.4839.zip -d /opt/sonar-scanner
echo "export PATH=$PATH:/opt/sonar-scanner/bin" >> ~/.bashrc
echo "export SONAR_SCANNER_HOME=/opt/sonar-scanner" >> ~/.bashrc
source ~/.bashrc
Maven Configuration:
Declare JaCoCo maven plugin in pom.xml file. Source:https://www.eclemma.org/jacoco/)
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>target/jacoco.exec</dataFile>
<outputDirectory>target/coverage-report</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Add Junit and sonar plugin to pom.xml file. (Source: https://mvnrepository.com/artifact/junit/junit and https://mvnrepository.com/artifact/org.sonarsource.scanner.maven/sonar-maven-plugin). Maven-surefire-plugin is responsible for running the Unit Tests and maven-failsafe-plugin is responsible for running the Integration test.
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>4.0.0.4121</version>
</dependency>
</dependencies>
Navigate to the project folder and execute SonarQube code analysis execution:
mvn clean verify sonar:sonar \
-Dsonar.projectKey='Javacoverage' \
-Dsonar.projectName='Javacoverage' \
-Dsonar.host.url='http://localhost:9000/' \
-Dsonar.branch.name='develop' \
-Dsonar.login='squ_454790fc796698fae62c9c52bbf477e164e87350' \
-DSonar.quality.gate.wait=true \
-Dsonar.scm.provider=git \
-Dsonar.junit.reportPaths=target/surefire-reports \
-Dsonar.jacoco.reportPaths=target/jacoco.exec \
-Dsonar.exclusions=''
Below dashboard shows the coverage report with all the issues, security vulnerabilities, maintainability metrics and code duplication blocks found in our code. Above code snippet can be easily integrated into CI pipelines and configured to enforce a condition to meet the desired code coverage through pull request based builds. Pull requests raised from the feature/developer branch get merged into the develop only if the build meets the Quality gate conditions. The screenshots below show the coverage report pushed into the Sonar portal.
In addition to java applications, code coverage snippet is also provided for python and .NET applications as a reference.
Code snippet for python applications:
export PATH="/opt/sonar-scanner/bin:$PATH"
sonar-sonar \
-Dsonar.projectKey='<Project Key>' \
-Dsonar.projectName='<ProjectName>' \
-Dsonar.host.url='http://localhost:9000/' \
-Dsonar.branch.name='<BranchName>' \
-Dsonar.login='squ_454790fc796698fae62c9c52bbf477e164e87350' \
-DSonar.quality.gate.wait=true \
-Dsonar.scm.provider=git \
-Dsonar.python.coverage.reportPaths=coverage.xml \
-Dsonar.python.xunit.reportPath=pytest.xml \
-Dsonar.python.xunit.skipDetails=true \
-Dsonar.sourceEncoding=UTF-8 \
-Dsonar.exclusions=''
Code snippet for .NET applications:
dotnet tool install --global dotnet-coverage
dotnet tool install --global dotnet-sonarscanner
dotnet tool list --global
export PATH="$PATH:$HOME/.dotnet/tools:$JAVA_HOME/bin"
cd <Unit test directory/solution directory/project directory>
dotnet sonarscanner begin /k:'<projectKey>' /d:sonar.host.url="" /d:sonar.branch.name='<BranchName>' /d:sonar.login='<login token>' /d:sonar.scm.provider.git=true /d:sonar.qualitygate.wait=true /d:sonar.cs.vstest.reportPaths="Testresults/*.trx" /d:sonar.cs.vscovergaexml.reportPaths=<Coverage output directory> /d:sonar.verbose=true /d:sonar.exclusions=""
dontnet build <solution file/project file> --no-incremental
dotnet-coverage collect -f xml --output <Coverage output directory> -- dotnet test --loger:trx
dotnet sonarscanner end /d:sonar.login='<login token>'
Summary
In summary, code coverage is vital for evaluating testing effectiveness. By using tools like JaCoCo and SonarQube, developers can identify potential bugs and vulnerabilities, ensuring high-quality code. Automating this process through CI pipelines enforces quality standards, promoting only code that meets coverage and quality criteria, leading to more reliable software.
GitHub reference for source code: https://github.com/Yogi-AK/CodeCoverage
Subscribe to my newsletter
Read articles from yogi k directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
