JUnit 5 dynamic tests

In JUnit 5, we can create dynamic tests that are generated at runtime by factory methods. These methods are annotated with the annotation TestFactory.

The method annotated with @TestFactory must return a Stream, Collection, Iterable, or Iterator of DynamicNode objects. DynamicNode is abstract. It has two subclasses, DynamicContainer is the container of other DynamicNode objects; DynamicTest is a generated test case. DynamicTest is created from a display name and an Executable object. Executable is a functional interface that contains only one method void execute(). DynamicContainer is created from a display name and a collection of DynamicNodes.

The code below shows a simple test factory. DynamicTest.dynamicTest(String displayName, Executable executable) is the static method to create DynamicTest objects.

1
2
3
4
5
6
7
8
@TestFactory
@DisplayName("simple")
List<DynamicTest> simpleDynamicTests() {
return Lists.newArrayList(
DynamicTest.dynamicTest("test1", () -> assertTrue(true)),
DynamicTest.dynamicTest("test2", () -> assertEquals(3, 1 + 2))
);
}

DynamicTest has another static method stream(Iterator<T> inputGenerator, Function<? super T, String> displayNameGenerator, ThrowingConsumer<? super T> testExecutor) to create a Stream of DynamicTests. The method stream requires three parameters. The first parameter is an Iterator<T> object that generates the input values. Each input value will be mapped to a generated test case. The second parameter is a Function<? super T, String> object that generates display names based on the input value. The third parameter is a ThrowingConsumer<? super T> object that contains the testing logic.

In the code below, the input values are integers from 1 to 10. The display names are generated using format Test - %s. The actual test is to verify the numbers are greater than 0.

1
2
3
4
5
6
7
8
9
@TestFactory
@DisplayName("test generators")
Stream<DynamicTest> testGenerators() {
return DynamicTest.stream(
IntStream.range(1, 10).iterator(),
num -> String.format("Test - %s", num),
num -> assertTrue(num > 0)
);
}

The figure below shows the result of running this test factory in IntelliJ IDEA.

We can also use static method DynamicContainer.dynamicContainer(String displayName, Stream<? extends DynamicNode> dynamicNodes) to create DynamicContainers. In the code below, we create nested tests using DynamicContainers and DynamicTests.

1
2
3
4
5
6
7
8
9
10
11
12
13
@TestFactory
@DisplayName("nested")
Stream<DynamicNode> nestedTests() {
return Stream.of(DynamicContainer.dynamicContainer("nested",
Stream.of(
DynamicContainer.dynamicContainer("t1",
Stream.of(
DynamicTest.dynamicTest("1", () -> assertTrue(true)),
DynamicTest.dynamicTest("2", () -> assertTrue(true))
)),
DynamicTest.dynamicTest("t2", () -> assertTrue(true))
)));
}

Check out the book JUnit 5 Cookbook for more new features in JUnit 5.

Comments