Maximizing Code Coverage: A Beginner’s Guide to Using Jacoco with Spring Boot

Maximizing Code Coverage: A Beginner’s Guide to Using Jacoco with Spring Boot

In a previous project I worked on, we had multiple release phases with monthly releases. One of the challenges we faced was adding new features or endpoints to existing microservices and introducing new microservices for new features without breaking previously released features. This required us to ensure that our code was thoroughly tested before each release. The Jacoco plugin for code coverage was incredibly helpful in this regard, as it allowed us to measure the effectiveness of our testing efforts by ensuring that the conditional and line coverage was always above 90% during the build phase of our pipeline.

Think of a Jacoco plugin as a code coverage “watchdog” for your Spring Framework project. Just like a watchful dog that guards your home, Jacoco helps you keep an eye on your codebase by measuring how much of it is covered by automated tests. With the Jacoco plugin integrated into your build process, you can be confident that your tests are thorough and effective, and that your code is reliable and performant. By generating detailed code coverage reports and visualizing the results in your preferred format, Jacoco ensures that your application is well-protected and ready for any challenge.

This helped us ensure that our tests were thorough and effective, and that our application was reliable and performant, even as we continued to add new features and microservices.

How to use Jacoco Plugin

To showcase how to use Jacoco plugin with a Spring Boot application, we will create a basic application with a single class called Metadata. This class contains a single method called getMetadata, which takes a string parameter called key and returns a string.

The method first checks if the key is valid, and if not, it returns an error message. Otherwise, it converts the key to uppercase and returns it. This method is simple but demonstrates the concept of adding code coverage to your application using Jacoco.

By using Jacoco, you can run automated tests on this method and generate code coverage reports. These reports will help you identify any areas of the code that are not being tested and need additional testing. This ensures that your application is thoroughly tested and ready for deployment.

Code for this demo is available on GitHub

  1. Add the Jacoco plugin to your project’s build configuration file (e.g. pom.xml for Maven).
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>

2. Configure the plugin by specifying the target execution phase and the coverage report format.

<execution>
      <id>report</id>
      <phase>test</phase>
      <goals>
         <goal>report</goal>
      </goals>
      <configuration>
       <excludes>
          <exclude>dev/knowledgecafe/JaCoCoDemoApplication.class</exclude>
       </excludes>
         <outputDirectory>target/jacoco-report</outputDirectory>
      </configuration>
</execution>

In above configuration we have configured to exclude spring boot application class which does not have any business logic and configure report generation directory.

3. Run your service’s automated tests with code coverage enabled. The Jacoco plugin will generate a coverage report that you can use to identify areas that need more testing.

    @Test
    public void testValidKeyMetadataValue()
    {
        Metadata obj = new Metadata();
        Assertions.assertEquals("MY VALUE", obj.getMetadata("My Value"));
    }

    @Test
    public void testBlankKeyMetadataValue()
    {
        Metadata obj = new Metadata();
        Assertions.assertEquals("Invalid key!", obj.getMetadata(""));
    }

    @Test
    public void testNullKeyMetadataValue()
    {
        Metadata obj = new Metadata();
        Assertions.assertEquals("Invalid key!", obj.getMetadata(null));
    }

4. Run the maven task clean and test to generate Jacoco report. If I comment out testNullKeyMetadataValue() method then generate report will show like below

How to Read Jacoco Report

The 28 instructions in JaCoCo refer to byte code instructions, not Java code instructions

Diamond icons with different colors in JaCoCo represent different levels of coverage

  • Red diamond means no branches have been exercised during testing

  • Yellow diamond means partially covered code with some untested branches

  • Green diamond means all branches have been exercised during testing

Background colors also represent different levels of coverage for lines. JaCoCo provides three important metrics: lines coverage, branches coverage, and cyclomatic complexity

  • Lines coverage shows the percentage of code exercised based on the number of Java byte code instructions called by the tests

  • Branches coverage shows the percentage of exercised branches in the code, usually related to if/else and switch statements

  • Cyclomatic complexity reflects code complexity by providing the number of paths needed to cover all possible paths in a code through a linear combination.

Once we add null check testcase report will look like below

Build Quality Enforcement

In addition to generating code coverage reports, we can configure Jacoco to enforce certain code coverage requirements for our Spring Boot application. For instance, we can set up Jacoco to prevent the build process from completing if the line coverage or conditional coverage does not meet the project’s requirements.

In the project the configuration was set to 90%, meaning that any code that has less than 90% code coverage will fail the build process. This ensures that any code changes or additions must have a sufficient level of automated testing before being integrated into the project. This can help prevent bugs and issues from arising in the future and ensure that the codebase remains maintainable and reliable.

To configure this setting, we can add the appropriate configuration to our build file, such as the pom.xml file for Maven. With this additional configuration, we can ensure that our Spring Boot application is thoroughly tested and meets the project’s code coverage requirements.

<execution>
      <id>jacoco-check</id>
      <goals>
       <goal>check</goal>
      </goals>
      <configuration>
       <excludes>
        <exclude>dev/knowledgecafe/JaCoCoDemoApplication.class</exclude>
       </excludes>
       <rules>
        <rule>
         <element>PACKAGE</element>
         <limits>
          <limit>
           <counter>LINE</counter>
           <value>COVEREDRATIO</value>
           <minimum>0.9</minimum>
          </limit>
         </limits>
        </rule>
       </rules>
      </configuration>
</execution>

If we run maven command clean package without full test coverage the build will fail

How to Exclude a Particular Class or Method

Sometimes, certain classes or methods in our code are not critical to our application’s functionality and their coverage can skew the overall code coverage report. For example, Data Transfer Object (DTO) classes typically contain only data and have no business logic, so their coverage may not be necessary. Similarly, the hashcode() and equals() methods are automatically generated by the IDE and their coverage may not be useful as they are not specific to the application's business logic.

Therefore, to avoid such irrelevant classes or methods from affecting the code coverage, we can exclude them from the coverage analysis using the Jacoco plugin. By excluding these classes or methods, we can get a more accurate representation of the coverage of our application’s critical components.

There are two ways to exclude classes or methods, one is through maven plugin exclusion like below

<configuration>
       <excludes>
         <exclude>dev/knowledgecafe/JaCoCoDemoApplication.class</exclude>
         <exclude>dev/knowledgecafe/Generated.class</exclude>
       </excludes>
</configuration>

Custom Annotation: Since JaCoCo 0.8.2, it is possible to exclude certain classes and methods from code coverage reports by creating a custom annotation with the name including “Generated” and the retention policy of “runtime” or “class”. This annotation can then be added to classes, methods or constructors that should be excluded from the coverage report


@Documented
@Retention(RUNTIME)
@Target({TYPE, METHOD, CONSTRUCTOR})
public @interface Generated {
}

@Generated // This whole Class will be excluded from Jacoco
public class PersonDTO {

    private String name;
    private String contact;

    @Generated // This method will be excluded from Jacoco
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        PersonDTO personDTO = (PersonDTO) o;

        if (name != null ? !name.equals(personDTO.name) : personDTO.name != null) return false;
        return contact != null ? contact.equals(personDTO.contact) : personDTO.contact == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (contact != null ? contact.hashCode() : 0);
        return result;
    }
}

For example, we can exclude all Lombok-generated bytecode by adding a property to the lombok.config file in the project’s root directory. This property adds the lombok.@Generated annotation to the relevant constructs of all the classes annotated with Lombok annotations, such as the Product class. As a result, JaCoCo will ignore all the constructs annotated with this annotation, and they won’t be shown in the coverage reports. It is necessary to exclude certain constructs like DTOs or hashcode/equals methods from code coverage because they don’t add value to the tests, and their coverage might skew the overall code coverage metrics.

Summary

The blog post discusses the importance of using the Jacoco code coverage tool with Spring Boot applications. The post explains how Jacoco can help measure the effectiveness of automated testing efforts, and how it can be configured to enforce code coverage requirements. The author also provides a step-by-step guide on how to integrate Jacoco with a Spring Boot application using Maven.

Bonus

🎉 Congratulations! You have made it to the end of the article! And here’s an exciting bonus 🎁 for you! You can get a FREE book 📚 with over 200 Spring interview questions! 💯💻

If you want to master tricky Spring topics and ace your next interview, don’t miss out on my eBook “Spring Interview Questions”. 🚀 Click on the link to download your copy and gain valuable insights for your next interview. 😍 Happy coding!

Did you find this article valuable?

Support Amit Himani by becoming a sponsor. Any amount is appreciated!