Junit 5 Test Report Using SonarQube Jacoco Gradle

Table of Contents

Introduction

In this Spring Boot based application I am going to show you how to generate Junit 5 test report using SonarQube Jacoco gradle build tool. You need to generate such test reports to ensure that your source code meets certain standard and quality to avoid maximum chance of vulnerabilities to threats. The maintenance of code quality is a continuous process which has to be applied periodically.

Related Posts:

Prerequisites

Java 11, SonarQube 9.4.0, Jacoco 0.8.8, Gradle 7.4.2, Spring Boot 2.6.6

Make sure you have setup SonarQube in your local environment and it’s running at http://localhost:9000 address.

Project Setup

I am using the following build.gradle script for configuring Sonar, Jacoco, Junit 5, Spring Boot for this application.

buildscript {
  	ext {
    	springBootVersion = '2.6.6'
  	}
 
  	repositories {
    	mavenCentral()
  	}
 
  	dependencies {
    	classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
    	classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3"
  	}
}
 
plugins {
    id 'java-library'
    id 'org.springframework.boot' version "${springBootVersion}"
    id "org.sonarqube" version "3.3"
    id 'jacoco'
}

jacoco {
    toolVersion = "0.8.8"
}

sonarqube {
    properties {
    	property "sonar.coverage.jacoco.xmlReportPath", "${project.buildDir}/reports/jacoco.xml"
    	property "sonar.junit.reportPaths", "${project.buildDir}/reports/jacoco.xml"
    	property "sonar.verbose", true
    	property "sonar.sources", "src/main"
    	property "sonar.tests", "src/test"
    	property "sonar.host.url", "http://localhost:9000"
        property "sonar.projectName", "Spring Junit 5 Sonar Jacoco Report"
        property "sonar.projectKey", "com.roytuts:spring-junit5-jacoco-report"
        property "sonar.login", "4b0f11b3e2ef5aa4b62144899af888baa85ee524"
    }
}

test {
	useJUnitPlatform()
	finalizedBy jacocoTestReport
}

jacocoTestReport {
    reports {
        xml.required = true
        html.outputLocation = layout.buildDirectory.dir('jacocoHtml')
    }
}
 
sourceCompatibility = 11
targetCompatibility = 11
 
repositories {
    mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
	implementation("org.springframework.boot:spring-boot-starter-test:${springBootVersion}")
}

I have not used separate dependencies for Junit 5 as the spring-boot-starter-test dependency includes Junit 5. If your application is not Spring Boot based then you need to use Junit 5 dependencies.

I have configured Sonar properties to have code analysis in Sonar dashboard, for example, code coverage, code smell, any issues, etc.

Service Class

For this example, I am going to use the following simple service class. The real world project will have a service class with probably more complex business logic.

package spring.junit5.jacoco.report.service;

import org.springframework.stereotype.Service;

@Service
public class CalculatorService {

	public int findMax(int arr[]) {
		int max = arr[0];
		for (int i = 1; i < arr.length; i++) {
			if (max < arr[i])
				max = arr[i];
		}
		return max;
	}

	public int getRemainder(int dividend, int divisor) {
		if (divisor <= 0) {
			throw new IllegalArgumentException("Divisor should be greater than 0");
		}
		return dividend % divisor;
	}

}

The Junit 5 Test Class

The following test class that determines the test coverage for the above service class:

package spring.junit5.jacoco.report;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Spy;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import spring.junit5.jacoco.report.service.CalculatorService;

@ExtendWith(SpringExtension.class)
public class CalculatorServiceTest {

	@Spy
	private CalculatorService calc;

	@Test
	public void testFindMax() {
		int result = calc.findMax(new int[] { 1, 3, 4, 2 });
		assertEquals(4, result);
		result = calc.findMax(new int[] { -12, -1, -3, -4, -2 });
		assertEquals(-1, result);
	}

	@Test
	public void testGetRemainder() {
		int result = calc.getRemainder(5, 2);
		assertEquals(1, result);
	}

	@Test
	public void testGetRemainderEx() {
		assertThrows(IllegalArgumentException.class, () -> calc.getRemainder(5, 0));
	}

}

The first two test methods are positive scenarios, whereas the last test method is when an exception is expected, for example, a number is divide by zero. In Junit 4 you might have used @Test(expected = <exception class>.class), but in Junit 5 you need to use assertThrows() to verify the expected exception.

Generating Test Reports

Now when you execute the command gradlew clean build on your project’s root directory from the CLI tool, your test report gets generated under <project root folder>/lib/build/jacocoHtml.

Next you can check the code coverage by clicking on the index.html file:

jacoco sonar junit 5

In the above image, you see that overall code coverage is 82% because service package has 100% code coverage though other package does not have any coverage.

You can further go inside the package and check which class has coverage. For spring.junit5.jacoco.report package you see that the class App has 0% coverage:

junit 5 jacoco

Whereas the CalculatorService class has 100% coverage:

jacoco spring boot junit 5

Scanning Project with Sonar

Next I am going to scan the project and I will make it appear on the Sonar dashboard. To do it I am executing the command gradlew sonarqube on the project’s root folder from CLI tool.

sonar jacoco code coverage

So, once build is successful, you will see the project in SonarQube’s dashboard section. The overall code coverage shown in the dashboard is 83.3%. Though you have seen the code coverage in your local report from Jacoco is 82%.

Now you can check for any issues. For example, this project has few issues as shown below:

sonar code analysis

So, these are minor issues, you can fix these issues as well and if you do not fix then also your project would be in good condition, but if your project has Major, Critical, Blocker then you must fix those issues.

Hope you got an idea how generate code coverage reports using Sonar Jacoco Junit 5 and Spring Boot in Java based applications.

Source Code

Download

Leave a Reply

Your email address will not be published.