JaCoCo Code Coverage Report in SonarCloud with Quarkus

you can also read this article in Medium -> click here
After I struggled a bit to set JaCoCo Code Coverage Report in SonarCloud, I decided to share my experience and present a minimal setup that consists of a small Quarkus Gradle Project with Unit- and Integration Tests to showcase the whole functionality.
This article is focused on the practical setup. By the time you’re done here, you’ll know exactly what to do to set the whole thing up. The steps should also be beginner friendly, as they explain everything in great detail. Let’s start!
1. Create a Quarkus Application
Go to code.quarkus.dev and configure the following application:

Here we just add the jacoco — Code Coverage
as a dependency for now. I use Java 21 for my project, but you can adjust this to your environment.
Now import the application in IntelliJ and you should have the following app structure:

2. Write some code
In order to write some tests, we need some code, so let’s create a Service Bean and annotate it with @ApplicationScoped
. In the src/main/java
folder, create a new Java Class named Service
and let’s code something small inside:
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class Service {
public String method(String arg) {
if( arg.equals("one") ) {
return "One";
}
if( arg.equals("two") ) {
return "Two";
}
if( arg.equals("three") ) {
return "Three";
}
return "default";
}
}
A quick explanation of what we have here:
I have created a simple class with a single method that contains some conditional statements, that can help us showcase our code coverage. We’ll check if our tests check every single branch and aim to achieve 100% code coverage.
3. Write Integration Tests with coverage
Create a new src/test/java
directory:

and now create a class called IntegrationTest with simple test cases that cover two of our if-
branches of the method in Service.java
class:
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@QuarkusTest
public class IntegrationTest {
@Inject
Service service;
@Test
void testOne(){
String result = service.method("one");
assertEquals("One", result);
}
@Test
void testTwo(){
String result = service.method("two");
assertEquals("Two", result);
}
}
Let’s take a step back here and understand what the @QuarkusTest
annotation does and why this is an integration test:
This annotation sets up the application in a quasi-runtime environment where Quarkus handles dependency injection (DI) for you. This test ensures that our @ApplicationScoped
bean (Service
) is managed by Quarkus's DI container. It is slower than a unit test because it involves the initialization of the Quarkus context.
Alright, let’s go and get our first code coverage report! Two of our Service method’s if-
branches should be covered by this test.
Execute ./gradlew test
in order to run the tests and let jacoco create the report:

Great! Now open the index.html
file under /jacoco-example/build/jacoco-report
and voila!

4. Write Unit Tests with coverage
Alright, let’s cover the rest of our method with some Unit Tests!
Create a class called UnitTest with some simple test cases that cover the rest of our method in the Service.java
class:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class UnitTest {
@Test
void testThree(){
Service service = new Service();
String result = service.method("three");
assertEquals("Three", result);
}
@Test
void testDefault(){
Service service = new Service();
String result = service.method("something");
assertEquals("default", result);
}
}
Before running the tests, add the following under in your build.gradle
file. This will let us see the test results in the terminal after executing the tests:
test {
systemProperty "java.util.logging.manager", "org.jboss.logmanager.LogManager"
testLogging {
events "PASSED", "FAILED", "SKIPPED" // Show results for these events
showStandardStreams = true // Show standard output and error streams
}
}
Now let’s run the tests:

Alright, all our tests pass, but you’ll notice that the jacoco report didn’t change at all!
Fortunately, the Quarkus documentation has this covered.
In order to check the coverage of tests, that are not annotated with @QuarkusTest
, you will need to fall back to the JaCoCo maven plugin. Here how our build.gradle
should look like:
plugins {
id 'java'
id 'io.quarkus'
id 'jacoco'
}
...
test {
systemProperty "java.util.logging.manager", "org.jboss.logmanager.LogManager"
testLogging {
events "PASSED", "FAILED", "SKIPPED" // Show results for these events
showStandardStreams = true // Show standard output and error streams
}
finalizedBy jacocoTestReport
jacoco {
excludeClassLoaders = ["*QuarkusClassLoader"]
destinationFile = layout.buildDirectory.file("jacoco-quarkus.exec").get().asFile
}
jacocoTestReport.enabled = false
}
And that was it! Just run ./gradlew test again and here’s our code coverage report:

5. Github Repo
In order to proceed, we need to add our code to a Github Repository
Create a new public Github Repository Add existing project to it. Go to your project folder and in the terminal:
git init
git add .
git commit -m “first commit”
git branch -M main
git remote add origin [email protected]:{github_username}/{github_repository}.git
git push -u origin main
That’s it, now that we have our code in github, we can proceed.
6. SonarCloud
Use your GitHub Account to Log In to SonarCloud. For public repositories, the SonarCloud Analysis is completely free!
Now go to My Projects -> Analyze new project and choose your Github Username as Organisation. You will then be asked to choose a repository and if you’re new to SonarCloud, you’ll have to configure a GitHub App:

Follow the link to Configure SonarCloud App in your GitHub Account. You then need to add the repository in the App in order for it to appear in the SonarCloud for choosing. You then need to follow the Set Up process which is very straight forward:

After a minute, you’ll be able to see the first analysis under “My Projects” in SonarCloud:

You’ll notice that the Coverage is 0.0% which is what we’re about to change now!
7. Quarkus Code Coverage with JaCoCo in SonarCloud
First, we need to add the sonarqube plugin to our build.gradle and setup the sonar gradle task:
plugins {
id 'java'
id 'io.quarkus'
id 'jacoco'
id("org.sonarqube") version "6.0.1.5171"
}
...
sonar {
properties {
property 'sonar.projectKey', 'YOUR_PROJECT_KEY'
property 'sonar.organization', 'YOUR_ORGANIZATION'
property 'sonar.host.url', 'https://sonarcloud.io'
property 'sonar.coverage.jacoco.xmlReportPaths', layout.buildDirectory.file('jacoco-report/jacoco.xml').get().asFile
}
}
The project key and the organization key you can get from SonarCloud. Go to your project and then Information:

Another small thing we need to do in order for our setup to work is to go to Administration -> Analysis Method and turn off Automatic Analysis. We’ll trigger the sonar analysis through our gradle task

Then you need a Token from SonarCloud which you can get by going to My Account -> Security. You give your token a name, generate it and save it somewhere safe!
All we need to to do now is go to our terminal and execute:
./gradlew sonar -Dsonar.token=YOUR_SONAR_TOKEN
And there we are! We can now go to our project in SonarCloud and see the Code Coverage:

Conclusion
Setting up JaCoCo code coverage reporting with SonarCloud for your Quarkus Gradle project doesn’t have to be a complex task. While the initial configuration might seem daunting, following these step-by-step instructions should get you through it pretty easily. Remember, good test coverage is just one aspect of code quality. Don’t follow it blindly! A 100% covered but poorly tested codebase is often worse than 60% coverage with meaningful business-oriented test cases that truly validate your application’s behaviour.
I hope this guide has simplified the integration process and saved you from the initial struggles I encountered. Happy coding!