JUnit 5 Dynamic Tests – @TestFactory, DynamicTest

Filed Under: JUnit

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.


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.

JUnit Dynamic Tests @TestFactory example

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:


public class MyUtils {

	public static int add(int x, int y) {
		return x+y;
	}
}

Here is the test factory method for above function.


@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.

JUnit 5 Dynamic Test Example

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.

You can check out the complete code from our JUnit 5 example project GitHub Repository.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages