I am trying to generate a code coverage report using Jacoco with JUnit 5 and Spring Boot. And I"m trying to use the DynamicTest feature of JUnit 5. It runs successfully but the Dynamic Tests are not covered in the test coverage report generated by jacoco. Is JUnit 5 Dynamic Test not covered by Jacoco at the moment?
Here is the code:
@RunWith(JUnitPlatform.class)
@SelectPackages("com.troll.jpt.abc.model")
@SelectClasses({Status.class})
public class DynamicModelTester {
private Status status;
@BeforeEach
public void setUp() {
status = new Status();
}
@TestFactory
public Stream<DynamicTest> checkDynamicTestsFromStream() {
List<String> input = Arrays.asList("abc");
List<String> output = Arrays.asList("abc");
status.setCode(input.get(0));
return input.stream().map(str -> DynamicTest.dynamicTest("status test", () -> {
assertEquals(output.get(0), status.getCode());
}));
}
}
The jacoco plugin I'm using is as :
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.2-SNAPSHOT</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!-- Sets the path to the file which contains the execution data. -->
<dataFile>target/jacoco.exec</dataFile>
<!-- Sets the output directory for the code coverage report. -->
<outputDirectory>target/jacoco-ut</outputDirectory>
</configuration>
</execution>
</executions>
<configuration>
<systemPropertyVariables>
<jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
</systemPropertyVariables>
</configuration>
</plugin>
It runs successfully but the Dynamic Tests are not covered in the test coverage report generated by jacoco.
jacoco-maven-plugin:report
doesn't display coverage in tests, it displays coverage of a target of a test - in your case target seems to be class Status
, whose definition is completely absent.
Generic answer on questions like "does JaCoCo support JUnit 5 Dynamic Test?" is: JaCoCo records fact that code was executed independently from the way how it was executed - via JUnit 4 or JUnit 5, or even manually or whatever the other way of execution.
And generic answer on questions like "why some code is not marked as covered?" is: make sure that code is actually executed, in case of automatic tests this means - make sure that these tests are actually executed.
But let's give a try with JUnit 5 Dynamic Test. In absence of src/main/java/Status.java
I'll assume that it is something like
public class Status {
private String code;
public void setCode(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
No idea why you need systemPropertyVariables
, snapshot version of jacoco-maven-plugin
, multiple executions of report
and redundant specification of dataFile
with its default value, so will also assume that complete pom.xml
after slight cleanup looks like
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>example</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.21.0</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.1</version>
<configuration>
<outputDirectory>target/jacoco-ut</outputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
After placing
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.SelectPackages;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
@RunWith(JUnitPlatform.class)
@SelectPackages("com.troll.jpt.abc.model")
@SelectClasses({Status.class})
public class DynamicModelTester {
private Status status;
@BeforeEach
public void setUp() {
status = new Status();
}
@TestFactory
public Stream<DynamicTest> dynamicTestsFromStream() {
List<String> input = Arrays.asList("abc");
List<String> output = Arrays.asList("abc");
status.setCode(input.get(0));
return input.stream().map(str -> DynamicTest.dynamicTest("status test", () -> {
assertEquals(output.get(0), status.getCode());
}));
}
}
into src/test/java/DynamicModelTester.java
and execution of mvn clean verify
:
[INFO] --- maven-surefire-plugin:2.21.0:test (default-test) @ example ---
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ example ---
[INFO] Building jar: /private/tmp/jacoco/target/example-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- jacoco-maven-plugin:0.8.1:report (default) @ example ---
[INFO] Skipping JaCoCo execution due to missing execution data file.
Last line is quite suspicious as well as absence of number of tests executed by maven-surefire-plugin
together with absence of target/surefire-reports/
.
Let's rename DynamicModelTester
into DynamicModelTest
and execute mvn clean verify
again:
[INFO] --- maven-surefire-plugin:2.21.0:test (default-test) @ example ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running DynamicModelTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.023 s - in DynamicModelTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ example ---
[INFO] Building jar: /private/tmp/jacoco/target/example-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- jacoco-maven-plugin:0.8.1:report (default) @ example ---
[INFO] Loading execution data file /private/tmp/jacoco/target/jacoco.exec
[INFO] Analyzed bundle 'example' with 1 classes
Compared to previous attempt test was executed this time. And coverage report target/jacoco-ut/default/Status.html
looks like:
I believe that the reason of the fact that test is not executed lies in a default value of includes
of maven-surefire-plugin
:
A list of elements specifying the tests (by pattern) that should be included in testing. When not specified and when the test parameter is not specified, the default includes will be
<includes> <include>**/Test*.java</include> <include>**/*Test.java</include> <include>**/*Tests.java</include> <include>**/*TestCase.java</include> </includes>
DynamicModelTester.java
doesn't match any of these patters, while DynamicModelTest.java
matches second, but I'll leave verification of this as a little exercise.