Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 61

Measuring Test Coverage

Recommended resources

Hello! Welcome to the final lesson in our module on testing Spring applications.

In the last few lessons, you've learned to write unit tests for services, integration tests for repositories (@DataJpaTest), integration tests for the web layer (@WebMvcTest), and full end-to-end tests for the entire application (@SpringBootTest). You've built a powerful suite of tests, but a crucial question remains: how do we know if our tests are effective? Are there critical parts of our code that we've missed?

This lesson will equip you with the tools to answer that question. Our goal is to learn how to measure test coverage to identify untested code paths. We will use a standard Java tool called JaCoCo to generate reports that visualize which parts of your code are executed by your tests and which are not.

1. What is Code Coverage and Why Does It Matter?

Before we jump into the configuration, let's establish a clear understanding of what code coverage is and why it's a valuable metric in professional software development.

Mastering Code Coverage with JaCoCo in Java 21 and ...

The article 'Mastering Code Coverage with JaCoCo' provides a concise introduction to the concept. It explains what code coverage is and why it's a key part of the development feedback loop.

Please read the sections 'What Is JaCoCo?' and 'Why Code Coverage Matters'. Focus on understanding the different types of coverage (line, branch) and the benefits it provides, such as uncovering untested code and building confidence.

To summarize, code coverage is a measurement of the percentage of your source code that is executed when your test suite runs. It doesn't tell you if your tests are correct, but it very effectively tells you what you haven't tested.

Key benefits include:

  • Identifying Gaps: It visually highlights lines and logical branches that are never touched by your tests.
  • Increasing Confidence: High coverage on critical business logic gives you more confidence when refactoring or adding new features, as you know you have a safety net.
  • Preventing "Dead Code": If a piece of code is never executed by tests or the application, coverage reports can help you identify it as potentially unnecessary.

Important Note: Aiming for 100% coverage is often not a productive goal. It can lead to writing trivial tests just to satisfy the metric. The real goal is to write meaningful tests that cover the critical and complex paths of your application. Coverage is a tool to guide you, not a target to be blindly pursued.


2. Setting up JaCoCo in a Maven Project

To measure coverage, we need to integrate the JaCoCo tool into our build process. Since we're using Maven, this is done by adding the jacoco-maven-plugin to our pom.xml file.

The video below provides a clear, step-by-step guide to adding and configuring the plugin.

How to run Unit Tests with JaCoCo, Maven and IntelliJ IDEA

The video 'How to run Unit Tests with JaCoCo, Maven and IntelliJ IDEA' by Begin Secure will walk you through the process of adding the JaCoCo plugin to your pom.xml.

Please watch from 02:47 to 04:54. The video demonstrates exactly how to add the <plugin> configuration for JaCoCo. Pay attention to the two <execution> blocks: prepare-agent: This goal instruments your code on the fly during the test phase, allowing JaCoCo to track which lines are executed. report: This goal takes the execution data and generates the human-readable HTML report after the tests are complete.

Here is a typical configuration you would add to the <plugins> section within the <build> tag of your pom.xml:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.11</version> <!-- Use a recent version -->
    <executions>
        <execution>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

After adding this to your pom.xml and reloading your Maven project, JaCoCo is ready to go.


3. Generating and Interpreting the Coverage Report

With the plugin configured, generating a report is simple. You just need to run your tests. The report goal is tied to a phase in the Maven lifecycle (like test or prepare-package), so it runs automatically.

You can generate the report by running:
mvn clean verify

The verify phase runs all preceding phases, including test, ensuring the report is generated after the tests have run.

Now, let's see how to find and understand the report.

How to run Unit Tests with JaCoCo, Maven and IntelliJ IDEA

The same video demonstrates how to find the generated HTML report and how to interpret its contents.

Watch from 04:54 to 06:18. This section shows you: Where to find the report: target/site/jacoco/index.html. How to navigate from the main dashboard into specific classes. How to interpret the color-coding for lines and branches (green for covered, yellow for partially covered, and red for uncovered).

As you saw in the video, the report gives you a detailed breakdown of several metrics.

JaCoCo Report Example

This image from the Baeldung article "Intro to JaCoCo" shows an example of a detailed class report. Note the color-coding: green for fully covered lines, yellow for partially covered branches, and red for uncovered lines.

The most important metrics are:

  • Line Coverage: What percentage of executable lines have been run?
  • Branch Coverage: For every if statement or switch case, have all possible paths (e.g., both the if and the else block) been executed? This is often a more telling metric than line coverage.
Test your understanding!

You run your tests and the JaCoCo report for a service class shows 100% line coverage but only 50% branch coverage. What does this likely mean, and what action should you take?

Show answer

This means that while every line of code in the class was executed at least once, there is at least one conditional statement (e.g., an if/else block) where only one of the branches was tested. For example, your tests might only cover the if condition but never trigger the else condition.

Action: You should analyze the report to find the yellow diamond indicating the partially covered branch. Then, write a new test case that provides input specifically designed to execute the untested branch.


4. Enforcing Coverage with Quality Gates

Manually checking a report is useful, but in a professional team environment, you want to automate quality enforcement. You can configure JaCoCo to act as a quality gate—an automated check that fails the build if code coverage drops below a specified threshold.

This is critical for CI/CD (Continuous Integration/Continuous Deployment) pipelines, as it prevents code with insufficient test coverage from being merged into the main codebase.

How to run Unit Tests with JaCoCo, Maven and IntelliJ IDEA

Let's return to the 'Begin Secure' video, which has an excellent segment on setting up a quality gate.

Watch from 07:09 to 09:37. This part explains how to add a new <execution> with the check goal. Focus on understanding the <rules> configuration, where you can set a minimum coverage ratio for different elements like lines or branches.

To implement a quality gate, you add another <execution> block to your JaCoCo plugin configuration in pom.xml:

<execution>
    <id>jacoco-check</id>
    <goals>
        <goal>check</goal>
    </goals>
    <configuration>
        <rules>
            <rule>
                <element>BUNDLE</element> <!-- Can be BUNDLE, PACKAGE, CLASS, etc. -->
                <limits>
                    <limit>
                        <counter>BRANCH</counter>
                        <value>COVEREDRATIO</value>
                        <minimum>0.80</minimum> <!-- Fails if branch coverage is less than 80% -->
                    </limit>
                    <limit>
                        <counter>LINE</counter>
                        <value>COVEREDRATIO</value>
                        <minimum>0.85</minimum> <!-- Fails if line coverage is less than 85% -->
                    </limit>
                </limits>
            </rule>
        </rules>
    </configuration>
</execution>

With this configuration, if you run mvn clean verify, the build will fail if the branch coverage for the entire project (BUNDLE) is below 80% or line coverage is below 85%. This provides an automated safety net to maintain test quality over time.


5. Practical Consideration: Excluding Classes

Often, a JaCoCo report will show low coverage because it includes classes that you don't intend to test, such as:

  • The main Application class (its main method is an entry point, not logic to be unit-tested).
  • Configuration classes.
  • DTOs (Data Transfer Objects) or Model classes that are simple data holders with no logic.

You can configure JaCoCo to ignore these classes, giving you a more accurate and meaningful coverage score.

Mastering Code Coverage with JaCoCo in Java 21 and ...

Let's refer back to the 'Mastering Code Coverage' article for a quick guide on how to exclude classes.

Please read the section 'The Right Solution: Leave Out Coverage'. It shows how to add an <excludes> tag within the plugin's <configuration> to prevent JaCoCo from analyzing specific files.

To exclude your main application class and all DTOs, for example, you would add a <configuration> section to your plugin:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.11</version>
    <configuration>
        <excludes>
            <exclude>**/your/package/MainApplication.class</exclude>
            <exclude>**/your/package/dto/*.class</exclude>
        </excludes>
    </configuration>
    <!-- ... your executions ... -->
</plugin>

This keeps your coverage reports focused on the business logic that truly requires testing.


Conclusion

Congratulations on completing the testing module! You are now equipped with a comprehensive understanding of how to ensure your Spring applications are robust and reliable.

Key Takeaways:

  • Code Coverage is a metric that measures how much of your code is executed by your tests. It's a powerful tool for finding untested code paths.
  • JaCoCo is the standard tool in the Java ecosystem for measuring code coverage. It integrates with Maven via the jacoco-maven-plugin.
  • The HTML Report (target/site/jacoco/index.html) provides a visual, line-by-line breakdown of what is and isn't covered. Pay close attention to branch coverage (yellow and red diamonds).
  • Quality Gates (check goal) can be configured to automatically fail the build if coverage drops below a defined threshold, ensuring consistent test quality.

What's Next?

With a solid foundation in building and testing applications, we will now move on to more advanced topics related to running your applications in real-world environments. In the next module, "Advanced Configuration, Caching, & Monitoring," we will start by learning how to externalize configuration using YAML and environment variables, a crucial skill for managing applications across different environments like development, testing, and production.