Tests Coverage with Jacoco, Unit and Integration Tests

In this article I show how to obtain the tests coverage with Jacoco, for both the unit and the integration tests. I show the configuration of the Surefire and Failsafe plugins to run the unit and integration tests separately. And finally I show how to use the Surefire and Failsafe plugins to feed Jacoco.

Content

  • Integration tests
  • Surefire and Failsafe Maven plugins
  • Jacoco

Check this video for more details.

All the code of this article is available in the following repository.

Integration tests

First of all, I’ve created some integration tests.

@ExtendWith(SpringExtension.class)
@SpringBootTest
@Transactional
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        TransactionalTestExecutionListener.class,
        DbUnitTestExecutionListener.class })
@DatabaseSetup("/dataset/users.xml")
public class AuthenticationControllerIT {

    public MockMvc mockMvc;

    @Autowired
    private WebApplicationContext context;

    private ObjectMapper objectMapper = new ObjectMapper();

    @BeforeEach
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .apply(springSecurity())
                .build();
    }

    @Test
    void testSignUp() throws Exception {
        // given
        SignUpDto signUpDto = SignUpDto.builder()
                .firstName("my first name")
                .lastName("my last name")
                .login("myLogin")
                .password("myPassword".toCharArray())
                .build();

        // when then
        mockMvc.perform(post("/v1/signUp")
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .content(objectMapper.writeValueAsBytes(signUpDto))
        ).andExpect(status().is(201))
                .andExpect(jsonPath("$.id", is(1000)));
    }
}

To identify an integration test, I need the SpringExtension. Before JUnit 5, it’s called the Runners and I need the @RunWith annotation. But with JUnit 5 I use the Extensions with the @ExtendWith annotation.

Now, instead of running the tests with Mockito, I run them with Spring Boot. That’s the reason to annotate the class with @SpringBootTest. This annotation will register this class to be able to receive beans.

As my integration test runs against a database, the H2 database which is an in-memory database, I will also need the @Transactional annotation to indicate to clean the database at the end of each test.

Finally, some @TestExecutionListeners to indicate the behavior with the database: to clean the context after each test, to take into account to recreate the database after each test, and more.

The last annotation, @DatabaseSetup, is to inject data in the database before running each test.

After adding the annotation to the classes, I’ve need to configure the MockMvc bean to use the Spring Security context. This way, each request will need the authentication to be performed, as in production.

Surefire and Failsafe Maven plugins

Why do i need those plugins? The Surefire plugin is the one dedicated to run the unit tests. If a single test fails, it will stop the complete Maven build. Because the unit tests are mandatory for the correct behavior of the application. If a single unit test fails, it means that the application isn’t stable.

On the other side, the Failsafe plugin will be used to run the integration test. Why? Because it will run all the integration tests and list the result at the end. And if there is almost a single integration test which fails, the Maven build will end with an error code.

How to configure them? I will filter the integration test in the Surefire plugin to run only the unit tests. Because the Surefire plugin is configured by default to run all the tests present in the test folder.

On the other side, the Failsafe plugin has an already configured filter to run only the integration tests, those ending by IT.java. This way, i have the Surefire which is only dedicated to the unit tests, and Failsafe which is only dedicated to the integration tests.

There are two Maven goals: test and integration-test. The Surefire plugin will be scoped to the test goal. This is already the default behavior. And for the Failsafe plugin, i must scope it to the integration-test goal.

Here are the modifications done in the plugins section in the pom.xml file:

     <build>
        <plugins>

            <!-- Used for unit tests -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>**/IT*.java</exclude>
                    </excludes>
                </configuration>
            </plugin>
            <!-- Used for integration tests -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <executions>
                    <execution>
                        <id>integration-tests</id>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
     </build>

Jacoco

Next step: Jacoco to generate the report about the coverage.

But how does Jacoco work? Jacoco has an agent which will be connected while running the tests. This agent will inspect which lines of code are run: which branch, which method, which classes were executed during the test. And at the end, it will compare how many lines of code were run and how many were missed. What i must do is plug this agent before the test start and generate a report when the test phase finishes. I must do it for both the unit and the integration test phases. So when running the Surefire and Failsafe plugins i must add an agent, i must accept a new argument.

 <build>
        <plugins>

            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.6</version>
                <configuration>
                    <excludes>
                        <exclude>**/dto/*.class</exclude>
                        <exclude>**/entities/*.class</exclude>
                        <exclude>**/exceptions/*.class</exclude>
                        <exclude>**/podam/*.class</exclude>
                    </excludes>
                </configuration>
                <executions>
                    <execution>
                        <id>pre-unit-test</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                        <configuration>
                            <destFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</destFile>
                            <propertyName>surefireArgLine</propertyName>
                        </configuration>
                    </execution>
                    <execution>
                        <id>post-unit-test</id>
                        <phase>test</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                        <configuration>
                            <dataFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</dataFile>
                            <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory>
                        </configuration>
                    </execution>
                    <execution>
                        <id>pre-integration-test</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                        <configuration>
                            <destFile>${project.build.directory}/coverage-reports/jacoco-it.exec</destFile>
                            <propertyName>failsafeArgLine</propertyName>
                        </configuration>
                    </execution>
                    <execution>
                        <id>post-integration-test</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                        <configuration>
                            <dataFile>${project.build.directory}/coverage-reports/jacoco-it.exec</dataFile>
                            <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            ...

And the usage of the new argument in the already existing Surefire and Failsafe plugins:

            ...

            <!-- Used for unit tests -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <argLine>${surefireArgLine}</argLine>
                    <excludes>
                        <exclude>**/IT*.java</exclude>
                    </excludes>
                </configuration>
            </plugin>
            <!-- Used for integration tests -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <executions>
                    <execution>
                        <id>integration-tests</id>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                        <configuration>
                            <argLine>${failsafeArgLine}</argLine>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
     </build>

And the reports will be located at “target/site/jacoco” after building the project with Maven, both unit and integration tests.

References

Repository


Never Miss Another Tech Innovation

Concrete insights and actionable resources delivered straight to your inbox to boost your developer career.

My New ebook, Best Practices To Create A Backend With Spring Boot 3, is available now.

Best practices to create a backend with Spring Boot 3

Leave a comment

Discover more from The Dev World - Sergio Lema

Subscribe now to keep reading and get access to the full archive.

Continue reading