Java Gradle project with quality gates
Setup
I have a SpringBoot (Java) project that’s build with gradle tools. I want to have quality gates integrated in the testing phase of the project form the very beginning. At this point in time I don’t run yet on-premise SonarQube or don’t have SonarCloud licence to execute quality gates. To prevent that the project gets neglected in early stages, I will show you how to integrate JaCoCo codecoverage and SonarLint tool into Gradle check stage.
SonarLint
Its meant to be used as a plugin in an IDE, to help developers avoid common mistakes before they even commit the code. Based on the answer on a community forum it is not possible to run SonarLint as the build task.
name.remal.sonarlint plugin
I found name.remal.sonarlint plugin that is able to run SonarLint check with the source code as Gradle task.
After plugin is included sonarlintMain task will become available in Gradle tasks.
I want this to be the subtask of check. Then every time tests will be run also sonarLint will verify source code.
plugins {
...
id "name.remal.sonarlint" version "1.0.189"
}
check.dependsOn sonarlintMain
Now lets say I am lazy, and I forgot to clean imports after removing PUT mapping in Controller, following message during the check task now warns me that SonarLint found issues.
Running ./gradlew check
will render broken check task
> Task :sonarlintMain FAILED
1 SonarLint violations were found
[priority 4] [java:S1128] /path/Controller.java:9
Unnecessary imports should be removed
The imports part of a file should be handled by the Integrated Development Environment (IDE), not
manually by the developer.
Unused and useless imports should not occur if that is the case.
Leaving them in reduces the code's readability, since their presence can be confusing.
Noncompliant Code Example
package my.company;
import java.lang.String; // Noncompliant; java.lang classes are always implicitly imported
import my.company.SomeClass; // Noncompliant; same-package files are always implicitly imported
import java.io.File; // Noncompliant; File is not used
import my.company2.SomeType;
import my.company2.SomeType; // Noncompliant; 'SomeType' is already imported
class ExampleClass {
public String someString;
public SomeType something;
}
Exceptions
Imports for types mentioned in comments, such as Javadocs, are ignored.
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':sonarlintMain'.
> 1 SonarLint violations were found
JaCoCo
The JaCoCo plugin provides code coverage metrics for Java code via integration with JaCoCo. I am not interested into generating coverage reports but rather in the functionality of enforcing coverage metrics into gradle build. This is possible with the JacocoCoverageVerification task. It is used to verify if code coverage metrics are met based on configured rules.
Example build script that is wiring JacocoCoverageVerification into check task and configures coverage rules.
plugins {
id 'jacoco'
}
check.dependsOn jacocoTestCoverageVerification
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.94
}
}
}
}
Following exception will be thrown during build step if test code coverage is not high enough and as a result the build will break.
> Task :jacocoTestCoverageVerification FAILED
[ant:jacocoReport] Rule violated for bundle app: lines covered ratio is 0.81, but expected minimum is 0.94
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':jacocoTestCoverageVerification'.
> Rule violated for bundle app: lines covered ratio is 0.81, but expected minimum is 0.94
To achieve such coverage in an Spring MVC application that is also communicating with other services over API clients I use:
- @MockMvcTest for testing controllers
- Wiremock for testing API client implementation with RestTemplate.
Gradle Build Script
This is how gradle build script looks like if both plugins are used. I immediately benefit from this setup as quality rules are explicitly defined and they will be validated at every build execution of the project.
plugins {
...
id 'jacoco'
id "name.remal.sonarlint" version "1.0.189"
}
check.dependsOn jacocoTestCoverageVerification, sonarlintMain
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.94
}
}
}
}
Sources
#Gradle #Build tool #SonarLint #Code coverage #Gradle Plugin