How to Use Dynamic Tests in JUnit 5: Syntax and Examples
In JUnit 5, use
@TestFactory to create dynamic tests that generate test cases at runtime. Return a Stream, Collection, or Iterable of DynamicTest objects, each with a display name and executable code block.Syntax
Dynamic tests in JUnit 5 are created inside methods annotated with @TestFactory. These methods return a Collection, Stream, or Iterable of DynamicTest objects. Each DynamicTest requires a display name and an executable lambda or method reference.
- @TestFactory: Marks a method that generates dynamic tests.
- DynamicTest.dynamicTest(String, Executable): Creates a single dynamic test with a name and code.
- Return type: Must be a
Collection,Stream, orIterableofDynamicTest.
java
import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import java.util.stream.Stream; import static org.junit.jupiter.api.DynamicTest.dynamicTest; class DynamicTestsExample { @TestFactory Stream<DynamicTest> dynamicTests() { return Stream.of( dynamicTest("Test 1", () -> { // test code here }), dynamicTest("Test 2", () -> { // test code here }) ); } }
Example
This example shows a dynamic test factory that tests if numbers are even. It generates tests for numbers 1 to 5, each checking if the number is even. The test names describe the number being tested.
java
import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; import java.util.stream.IntStream; import java.util.stream.Stream; import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.junit.jupiter.api.Assertions.assertTrue; class EvenNumberTests { @TestFactory Stream<DynamicTest> testEvenNumbers() { return IntStream.rangeClosed(1, 5) .mapToObj(n -> dynamicTest("Is " + n + " even?", () -> { assertTrue(n % 2 == 0, n + " is not even"); })); } }
Output
Test run results:
- Is 1 even? FAILED (1 is not even)
- Is 2 even? PASSED
- Is 3 even? FAILED (3 is not even)
- Is 4 even? PASSED
- Is 5 even? FAILED (5 is not even)
Common Pitfalls
- Not annotating the method with
@TestFactorywill cause tests not to run. - Returning a type other than
Collection,Stream, orIterableofDynamicTestcauses errors. - Using
@Testinstead of@TestFactoryfor dynamic tests is incorrect. - Dynamic tests must be generated at runtime; returning an empty collection means no tests run.
java
import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; import java.util.stream.Stream; import static org.junit.jupiter.api.DynamicTest.dynamicTest; class WrongDynamicTest { // Wrong: Using @Test instead of @TestFactory @Test Stream<DynamicTest> dynamicTests() { return Stream.of(dynamicTest("Test", () -> {})); } } // Correct way: import org.junit.jupiter.api.TestFactory; class CorrectDynamicTest { @TestFactory Stream<DynamicTest> dynamicTests() { return Stream.of(dynamicTest("Test", () -> {})); } }
Quick Reference
| Concept | Description |
|---|---|
| @TestFactory | Marks method that generates dynamic tests |
| DynamicTest.dynamicTest(name, executable) | Creates a single dynamic test |
| Return types | Stream, Collection, or Iterable of DynamicTest |
| Test names | Should be descriptive for clarity in reports |
| Assertions | Use standard JUnit assertions inside dynamic tests |
Key Takeaways
Use @TestFactory to create methods that generate dynamic tests at runtime.
Return a Stream, Collection, or Iterable of DynamicTest objects with descriptive names.
Each DynamicTest needs a display name and executable code (lambda or method reference).
Avoid using @Test annotation for dynamic test methods; it must be @TestFactory.
Dynamic tests help test multiple cases without writing separate test methods.