Introduction
In this chapter, we will explore JUnit dynamic tests. Dynamic tests allow you to create and execute tests programmatically at runtime. This is useful when the number of tests or their inputs are not known until execution time, providing flexibility and power in your testing strategy.
What are Dynamic Tests?
Dynamic tests in JUnit are tests that are generated at runtime using the DynamicTest
class. Unlike static tests, which are defined at compile-time, dynamic tests allow you to create tests programmatically. This is particularly useful for scenarios where the test cases need to be generated based on external data or runtime conditions.
Dynamic tests are created using the DynamicTest
class and the dynamicTest
factory methods provided by JUnit. These tests can be grouped and returned as a collection of DynamicNode
instances from a factory method annotated with @TestFactory
.
Example: Creating Dynamic Tests
Let’s create an example to demonstrate how to use dynamic tests in JUnit.
Step 1: Create a Class Under Test
Calculator Class
The Calculator
class will have basic arithmetic operations.
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
public int multiply(int a, int b) {
return a * b;
}
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Division by zero");
}
return a / b;
}
}
Step 2: Create a Test Class with Dynamic Tests
CalculatorTest
The CalculatorTest
class will contain dynamic tests for the Calculator
class.
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.function.Executable;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
public class CalculatorTest {
private final Calculator calculator = new Calculator();
@TestFactory
Collection<DynamicTest> dynamicTestsForAddition() {
return Arrays.asList(
dynamicTest("1 + 1 = 2", () -> assertEquals(2, calculator.add(1, 1))),
dynamicTest("2 + 3 = 5", () -> assertEquals(5, calculator.add(2, 3))),
dynamicTest("3 + 5 = 8", () -> assertEquals(8, calculator.add(3, 5)))
);
}
@TestFactory
Stream<DynamicTest> dynamicTestsForSubtraction() {
return Stream.of(
new TestCase(5, 3, 2),
new TestCase(10, 5, 5),
new TestCase(3, 2, 1)
).map(testCase -> dynamicTest(
testCase.inputA + " - " + testCase.inputB + " = " + testCase.expected,
() -> assertEquals(testCase.expected, calculator.subtract(testCase.inputA, testCase.inputB))
));
}
static class TestCase {
final int inputA;
final int inputB;
final int expected;
TestCase(int inputA, int inputB, int expected) {
this.inputA = inputA;
this.inputB = inputB;
this.expected = expected;
}
}
}
Important Points
- @TestFactory: Use this annotation to mark a method as a factory for dynamic tests. The method must return a
Stream
,Collection
,Iterable
, or array ofDynamicNode
instances. - DynamicTest: Use the
DynamicTest.dynamicTest
factory method to create dynamic tests. Each dynamic test requires a display name and anExecutable
(typically a lambda) containing the test logic.
Running the Dynamic Tests
To run the dynamic tests, simply run the CalculatorTest
class as a JUnit test. This will execute all dynamic test methods, generating and running tests at runtime.
Using IntelliJ IDEA
- Run Tests: Click the green run icon next to the
CalculatorTest
class and selectRun
. - View Results: The results will be displayed in the Run window, showing the dynamically generated tests.
Using VS Code
- Run Tests: Open the
CalculatorTest
file and click theRun
icon above the class declaration. - View Results: The results will be displayed in the Test Explorer, showing the dynamically generated tests.
Using Eclipse
- Run Tests: Right-click on the
CalculatorTest
file and selectRun As
>JUnit Test
. - View Results: The results will be displayed in the JUnit view, showing the dynamically generated tests.
Conclusion
JUnit dynamic tests provide a flexible way to generate and run tests programmatically at runtime. By using the @TestFactory
annotation and the DynamicTest
class, you can create dynamic tests based on runtime conditions or external data, avoiding the need for duplicative test methods. This approach helps in creating more maintainable and adaptable test suites for complex testing scenarios.