JUnit @TestFactory annotation coupled with DynamicTest
can be used to create a test factory method.
JUnit Dynamic Tests
JUnit @TestFactory methods must not be private or static. These methods must return a Stream, Collection, Iterable, or Iterator of DynamicNode
instances.
Any Stream returned by a @TestFactory will be properly closed by calling stream.close()
, making it safe to use a resource such as Files.lines()
as the initial source of the stream.
DynamicTest
is one of the implementation of DynamicNode
. Note that dynamic tests are different from @Test cases since callback methods such as @BeforeEach and @AfterEach are not executed for dynamic tests.
JUnit @TestFactory DynamicTest Example
Let’s look at a simple example of using @TestFactory and DynamicTest to create test factory of dynamic tests.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
package com.journaldev.dynamictests; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.DynamicTest.dynamicTest; import java.util.Arrays; import java.util.Collection; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.function.Executable; public class JUnit5DynamicTests { @TestFactory Collection<DynamicTest> dynamicTests() { return Arrays.asList( dynamicTest("simple dynamic test", () -> assertTrue(true)), dynamicTest("My Executable Class", new MyExecutable()), dynamicTest("Exception Executable", () -> {throw new Exception("Exception Example");}), dynamicTest("simple dynamic test-2", () -> assertTrue(true)) ); } } class MyExecutable implements Executable { @Override public void execute() throws Throwable { System.out.println("Hello World!"); } } |
Below image shows the output of the JUnit test execution.
Above example is very simple one, let’s create Dynamic tests for a custom class method. Let’s say we have a utility class defined as:
1 2 3 4 5 6 7 |
public class MyUtils { public static int add(int x, int y) { return x+y; } } |
Here is the test factory method for above function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@TestFactory Stream<DynamicTest> dynamicTestsExample() { List<Integer> input1List = Arrays.asList(1,2,3); List<Integer> input2List = Arrays.asList(10,20,30); List<DynamicTest> dynamicTests = new ArrayList<>(); for(int i=0; i < input1List.size(); i++) { int x = input1List.get(i); int y = input2List.get(i); DynamicTest dynamicTest = dynamicTest("Dynamic Test for MyUtils.add("+x+","+y+")", () ->{assertEquals(x+y,MyUtils.add(x,y));}); dynamicTests.add(dynamicTest); } return dynamicTests.stream(); } |
Below image shows the execution output of above test method.
Notice that our add method is simple, so in assertEquals()
we are using input variables to derive the expected output. If it’s a complex method, we can define a List for expected output and use that in the assertions. We can also define a custom Executable class if we want to have complex testing logic.
Summary
JUnit 5 Dynamic tests functionality can be achieved by parameterized tests. Also, Parameterized tests follow the standard JUnit test lifecycle and @BeforeEach and @AfterEach methods are executed for them. Whereas dynamic tests lifecycle is totally different and they don’t have access to @BeforeEach and @AfterEach methods.