JaCoCo Code Coverage Report in SonarCloud with Quarkus

Article Header

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:

Article Image

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:

Article Image

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:

Article Image

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:

Article Image

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

Article Image

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:

Article Image

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:

Article Image

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:

Article Image

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:

Article Image

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

Article Image

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:

Article Image

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

Article Image

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:

Article Image

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!