Jenkins, JaCoCo, and SonarQube Integration With Maven
Jenkins, SonarQube, and Jacoco are excellent tools for deploying applications. Check out these awesome Maven integrations.
Join the DZone community and get the full member experience.
Join For FreeUsing Jenkins to build your application, running tests with Jacoco code coverage, making SonarQube analysis, and saving all results to SonarQube online is a great way of deploying your applications.
In this article you will be able to use Jenkins with Maven and manage following tasks:
Build application
Running tests with Jacoco test coverage report
Running SonarQube analysis
Pushing test result, test coverage report and SonarQube analysis report to SonarQube Server.
Other common tasks like pulling source codes from Version Control System or deploying the war to the Application Server is out of scope of this article.
Jenkins
Jenkins is an award-winning, cross-platform, continuous integration and continuous delivery application. Use Jenkins to build and test your software projects continuously making it easier for developers to integrate changes to the project, and making it easier for users to obtain a fresh build. It also allows you to continuously deliver your software by providing powerful ways to define your build pipelines and integrating with a large number of testing and deployment technologies.
First you need a Jenkins instance. If you already have one you can use it. You can download the latest release from https://jenkins-ci.org/
Jenkins Sonar Plugin
Install Jenkins "SonarQube Plugin" with Update Center (not Sonargraph). Plugin url:
https://wiki.jenkins-ci.org/display/JENKINS/SonarQube+plugin
SonarQube
SonarQube is an open platform to manage code quality. As such, it covers the 7 axes: Duplicated code, Coding standards, Unit tests, Complex code, Potential bugs, Comments, Design and architecture
If you don't have a SonarQube instance you need to install one from http://www.sonarqube.org/.
SonarQube server configuration In Jenkins
To add a new SonarQube server follow "Manage Jenkins -> Configure System" and find “Add Sonar” option. Under the Advanced link fill in the server properties:
Name: MySonarServer
Server URL: http://10.210.99.88:9000
Sonar account login: sonar
Sonar account password: myPass
Database URL: jdbc:mysql://10.210.99.88:3306/sonar?useUnicode=true&characterEncoding=utf8
Database login: sonar
Database password: myDbPass
Database driver: com.mysql.jdbc.Driver
Coverage Option for Build Script
Add "verify -Prun-its,coverage" maven build option to catch test coverage report. Example:
Failed Test Behavior
Maven has a parameter to stop or continue after failed tests. In ideal case if the tests fail, it is better to stop builds. In Jenkins, you can set this option with "Build-> Advanced-> MAVEN_OPTS" and append the following parameter.
-Dmaven.test.failure.ignore=true
Adding SonarQube to Jenkins Post-Build
You can add Sonar analysis to Jenkins with “Add Post-build Actions” option. This is a list, so if there is more than one Sonar installation choose the one you added.
POM Updates
Inside the <properties> tag add SonarQube project properties. If you do not set any property SonarQube will use the default value.
<!-- This format is used by SonarQube. If you need another format see "buildnumber-maven-plugin" -->
<maven.build.timestamp.format>MM.yyyy</maven.build.timestamp.format>
<yearMonth>${maven.build.timestamp}</yearMonth>
<!-- ************************-->
<!-- Sonar/Reporting settings -->
<!-- ************************-->
<!-- Sonar/Jacoco integration. Note that these properties need to be defined outside the "coverage" profile
because we want to be to able to execute mvn sonar:sonar without passing a profile -->
<!-- Tells Sonar to use jacoco for coverage results -->
<sonar.projectKey>MyProjectKey</sonar.projectKey>
<sonar.projectName>My Project</sonar.projectName>
<sonar.projectVersion>${yearMonth}</sonar.projectVersion>
<sonar.language>java</sonar.language>
<sonar.sourceEncoding>UTF-8</sonar.sourceEncoding>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<!-- Jacoco version to use -->
<jacoco.version>0.7.2.201409121644</jacoco.version>
<!-- The Sonar Jacoco Listener for JUnit to extract coverage details per test -->
<sonar-jacoco-listeners.version>1.4</sonar-jacoco-listeners.version>
<!-- Don't let Sonar execute tests. We will ask it to Maven 'sonar.dynamicAnalysis' is deprecated since version 4.3 and should no longer be used. -->
<!-- <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis> -->
<!-- The system property jacoco.outputDir needs to be override on the command line
with an absolute path if you want to merge results from all modules.
Example in a Jenkisn build where ${WORKSPACE} is defined and your project in the root directory of the workspace :
mvn clean install -Prun-its,coverage -Djacoco.outputDir=${WORKSPACE}/target
Note that unfortunately using the following does not work because of
http://jira.codehaus.org/browse/SONAR-3427:
<jacoco.outputDir>${session.executionRootDirectory}/target/</jacoco.outputDir>
-->
<jacoco.outputDir>${project.build.directory}</jacoco.outputDir>
<!-- Jacoco output file for UTs -->
<jacoco.out.ut.file>jacoco-ut.exec</jacoco.out.ut.file>
<!-- Tells Sonar where the Jacoco coverage result file is located for Unit Tests -->
<sonar.jacoco.reportPath>${jacoco.outputDir}/${jacoco.out.ut.file}</sonar.jacoco.reportPath>
<!-- Jacoco output file for ITs -->
<jacoco.out.it.file>jacoco-it.exec</jacoco.out.it.file>
<!-- Tells Sonar where the Jacoco coverage result file is located for Integration Tests -->
<sonar.jacoco.itReportPath>${jacoco.outputDir}/${jacoco.out.it.file}</sonar.jacoco.itReportPath>
<!-- <sonar.junit.reportsPath>${project.build.directory}/surefire-reports/</sonar.junit.reportsPath> -->
<!-- <sonar.tests>src/test/java</sonar.tests> -->
<!-- === END of Sonar/Reporting settings === -->
For the Jacoco code coverage add the following code to profiles tag
<!-- coverage -->
<profile>
<id>coverage</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>${jacoco.agent.ut.arg}</argLine>
<!-- Specific to generate mapping between tests and covered code -->
<properties>
<property>
<name>listener</name>
<value>org.sonar.java.jacoco.JUnitListener</value>
</property>
</properties>
<!-- test failure ignore -->
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<argLine>-Xmx1024m -XX:MaxPermSize=256m ${jacoco.agent.it.arg}
</argLine>
<!-- Specific to generate mapping between tests and covered code -->
<properties>
<property>
<name>listener</name>
<value>org.sonar.java.jacoco.JUnitListener</value>
</property>
</properties>
<!-- Let's put failsafe reports with surefire to have access to tests
failures/success reports in sonar -->
<reportsDirectory>${project.build.directory}/surefire-reports
</reportsDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions>
<!-- Prepares a variable, jacoco.agent.ut.arg, that contains the info
to be passed to the JVM hosting the code being tested. -->
<execution>
<id>prepare-ut-agent</id>
<phase>process-test-classes</phase>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>${sonar.jacoco.reportPath}</destFile>
<propertyName>jacoco.agent.ut.arg</propertyName>
<append>true</append>
</configuration>
</execution>
<!-- Prepares a variable, jacoco.agent.it.arg, that contains the info
to be passed to the JVM hosting the code being tested. -->
<execution>
<id>prepare-it-agent</id>
<phase>pre-integration-test</phase>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>${sonar.jacoco.itReportPath}</destFile>
<propertyName>jacoco.agent.it.arg</propertyName>
<append>true</append>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.codehaus.sonar-plugins.java</groupId>
<artifactId>sonar-jacoco-listeners</artifactId>
<version>${sonar-jacoco-listeners.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<!-- Integraton tests -->
<profile>
<id>run-its</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<id>integration-test</id>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
<execution>
<id>verify</id>
<phase>verify</phase>
<goals>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
When you complete all these steps, you will be able to run SonarQube analysis for each deployment. And plus, you will see test report and test code coverage in SonarQube project page.
You can see the screenshots of the result:
Jenkins Unit Test Report
Test and Coverage Summary
Unit Test Report
Unit Test Coverage Report
Opinions expressed by DZone contributors are their own.
Comments