0
# Dynamic Tests
1
2
Runtime test generation capabilities that allow creating tests programmatically during execution. Dynamic tests enable flexible test scenarios based on runtime data and conditions.
3
4
## Imports
5
6
```java
7
import org.junit.jupiter.api.*;
8
import java.util.stream.Stream;
9
import java.util.Collection;
10
import static org.junit.jupiter.api.Assertions.*;
11
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
12
import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
13
```
14
15
## Capabilities
16
17
### Test Factory Annotation
18
19
Mark methods that generate dynamic tests at runtime.
20
21
```java { .api }
22
/**
23
* Marks a method as a factory for dynamic tests
24
* Method must return Stream, Collection, Iterable, or Iterator of DynamicNode
25
*/
26
@Target(ElementType.METHOD)
27
@Retention(RetentionPolicy.RUNTIME)
28
@interface TestFactory {
29
}
30
```
31
32
### Dynamic Test Creation
33
34
Create individual dynamic test instances.
35
36
```java { .api }
37
/**
38
* A dynamically generated test
39
*/
40
class DynamicTest extends DynamicNode {
41
/**
42
* Create a dynamic test with display name and executable
43
*/
44
static DynamicTest dynamicTest(String displayName, Executable executable);
45
46
/**
47
* Create a dynamic test with display name, URI, and executable
48
*/
49
static DynamicTest dynamicTest(String displayName, URI testSourceUri, Executable executable);
50
51
/**
52
* Create a stream of dynamic tests from input data
53
*/
54
static <T> Stream<DynamicTest> stream(Iterator<T> inputGenerator,
55
Function<? super T, String> displayNameGenerator,
56
ThrowingConsumer<? super T> testExecutor);
57
58
/**
59
* Create a stream of dynamic tests from input stream
60
*/
61
static <T> Stream<DynamicTest> stream(Stream<T> inputStream,
62
Function<? super T, String> displayNameGenerator,
63
ThrowingConsumer<? super T> testExecutor);
64
}
65
```
66
67
**Usage Examples:**
68
69
```java
70
class DynamicTestExample {
71
72
@TestFactory
73
Stream<DynamicTest> simpleTests() {
74
return Stream.of("apple", "banana", "cherry")
75
.map(fruit -> DynamicTest.dynamicTest(
76
"test " + fruit,
77
() -> {
78
assertNotNull(fruit);
79
assertTrue(fruit.length() > 3);
80
}
81
));
82
}
83
84
@TestFactory
85
Collection<DynamicTest> numbersTest() {
86
return Arrays.asList(
87
DynamicTest.dynamicTest("Test 1", () -> assertEquals(2, 1 + 1)),
88
DynamicTest.dynamicTest("Test 2", () -> assertEquals(4, 2 * 2)),
89
DynamicTest.dynamicTest("Test 3", () -> assertEquals(9, 3 * 3))
90
);
91
}
92
93
@TestFactory
94
Stream<DynamicTest> dataStreamTests() {
95
List<String> data = Arrays.asList("alpha", "beta", "gamma");
96
97
return DynamicTest.stream(
98
data.stream(),
99
name -> "Processing " + name,
100
value -> {
101
assertNotNull(value);
102
assertTrue(value.length() > 3);
103
assertFalse(value.isEmpty());
104
}
105
);
106
}
107
}
108
```
109
110
### Dynamic Container Creation
111
112
Group related dynamic tests in containers.
113
114
```java { .api }
115
/**
116
* A container for dynamic tests or other containers
117
*/
118
class DynamicContainer extends DynamicNode {
119
/**
120
* Create a dynamic container with display name and children
121
*/
122
static DynamicContainer dynamicContainer(String displayName, Stream<DynamicNode> children);
123
124
/**
125
* Create a dynamic container with display name, URI, and children
126
*/
127
static DynamicContainer dynamicContainer(String displayName, URI testSourceUri, Stream<DynamicNode> children);
128
129
/**
130
* Create a dynamic container with display name and iterable children
131
*/
132
static DynamicContainer dynamicContainer(String displayName, Iterable<DynamicNode> children);
133
134
/**
135
* Create a dynamic container with display name, URI, and iterable children
136
*/
137
static DynamicContainer dynamicContainer(String displayName, URI testSourceUri, Iterable<DynamicNode> children);
138
}
139
```
140
141
**Usage Examples:**
142
143
```java
144
class DynamicContainerExample {
145
146
@TestFactory
147
Stream<DynamicNode> nestedTests() {
148
return Stream.of(
149
DynamicContainer.dynamicContainer("String Tests", Stream.of(
150
DynamicTest.dynamicTest("Test empty string", () -> assertTrue("".isEmpty())),
151
DynamicTest.dynamicTest("Test non-empty string", () -> assertFalse("hello".isEmpty()))
152
)),
153
154
DynamicContainer.dynamicContainer("Number Tests", Stream.of(
155
DynamicTest.dynamicTest("Test positive", () -> assertTrue(5 > 0)),
156
DynamicTest.dynamicTest("Test negative", () -> assertTrue(-5 < 0))
157
))
158
);
159
}
160
161
@TestFactory
162
Stream<DynamicNode> hierarchicalTests() {
163
return Stream.of("Category A", "Category B")
164
.map(category -> DynamicContainer.dynamicContainer(
165
category,
166
IntStream.range(1, 4)
167
.mapToObj(i -> DynamicTest.dynamicTest(
168
category + " Test " + i,
169
() -> {
170
assertNotNull(category);
171
assertTrue(i > 0);
172
}
173
))
174
));
175
}
176
}
177
```
178
179
### Dynamic Node Base
180
181
Base class for all dynamic test elements.
182
183
```java { .api }
184
/**
185
* Base class for dynamic tests and containers
186
*/
187
abstract class DynamicNode {
188
/**
189
* Get display name
190
*/
191
String getDisplayName();
192
193
/**
194
* Get test source URI
195
*/
196
Optional<URI> getTestSourceUri();
197
}
198
```
199
200
### Named Interface
201
202
Interface for providing names to test components.
203
204
```java { .api }
205
/**
206
* Interface for named test components
207
*/
208
interface Named {
209
/**
210
* Get the name
211
*/
212
String getName();
213
214
/**
215
* Create Named instance with given name and payload
216
*/
217
static <T> Named<T> of(String name, T payload);
218
}
219
220
/**
221
* Named executable for dynamic test creation
222
*/
223
interface NamedExecutable extends Named {
224
/**
225
* Get the executable
226
*/
227
Executable getExecutable();
228
229
/**
230
* Create NamedExecutable with name and executable
231
*/
232
static NamedExecutable of(String name, Executable executable);
233
}
234
```
235
236
**Usage Examples:**
237
238
```java
239
class NamedTestExample {
240
241
@TestFactory
242
Stream<DynamicTest> namedTests() {
243
List<Named<String>> testData = Arrays.asList(
244
Named.of("First test", "alpha"),
245
Named.of("Second test", "beta"),
246
Named.of("Third test", "gamma")
247
);
248
249
return testData.stream()
250
.map(namedData -> DynamicTest.dynamicTest(
251
namedData.getName(),
252
() -> {
253
String value = namedData.getPayload();
254
assertNotNull(value);
255
assertTrue(value.length() > 3);
256
}
257
));
258
}
259
260
@TestFactory
261
Stream<DynamicTest> namedExecutableTests() {
262
List<NamedExecutable> executables = Arrays.asList(
263
NamedExecutable.of("Test addition", () -> assertEquals(4, 2 + 2)),
264
NamedExecutable.of("Test subtraction", () -> assertEquals(0, 2 - 2)),
265
NamedExecutable.of("Test multiplication", () -> assertEquals(4, 2 * 2))
266
);
267
268
return executables.stream()
269
.map(namedExec -> DynamicTest.dynamicTest(
270
namedExec.getName(),
271
namedExec.getExecutable()
272
));
273
}
274
}
275
```
276
277
### Complex Dynamic Test Scenarios
278
279
Advanced patterns for dynamic test generation.
280
281
**Database-driven Tests:**
282
283
```java
284
class DatabaseDrivenTests {
285
286
@TestFactory
287
Stream<DynamicTest> testAllUsers() {
288
UserRepository repository = new UserRepository();
289
290
return repository.findAll().stream()
291
.map(user -> DynamicTest.dynamicTest(
292
"Validate user: " + user.getUsername(),
293
() -> {
294
assertNotNull(user.getEmail());
295
assertTrue(user.getAge() >= 0);
296
assertFalse(user.getUsername().isEmpty());
297
}
298
));
299
}
300
301
@TestFactory
302
Stream<DynamicNode> testUsersByRole() {
303
UserRepository repository = new UserRepository();
304
Map<String, List<User>> usersByRole = repository.findAll().stream()
305
.collect(Collectors.groupingBy(User::getRole));
306
307
return usersByRole.entrySet().stream()
308
.map(entry -> DynamicContainer.dynamicContainer(
309
"Role: " + entry.getKey(),
310
entry.getValue().stream()
311
.map(user -> DynamicTest.dynamicTest(
312
"Test " + user.getUsername(),
313
() -> validateUserInRole(user, entry.getKey())
314
))
315
));
316
}
317
318
private void validateUserInRole(User user, String expectedRole) {
319
assertEquals(expectedRole, user.getRole());
320
assertNotNull(user.getPermissions());
321
assertFalse(user.getPermissions().isEmpty());
322
}
323
}
324
```
325
326
**Configuration-based Tests:**
327
328
```java
329
class ConfigurationBasedTests {
330
331
@TestFactory
332
Stream<DynamicTest> testConfigurations() throws IOException {
333
Properties configs = loadTestConfigurations();
334
335
return configs.entrySet().stream()
336
.map(entry -> DynamicTest.dynamicTest(
337
"Test config: " + entry.getKey(),
338
() -> {
339
String key = (String) entry.getKey();
340
String value = (String) entry.getValue();
341
342
assertNotNull(value, "Configuration value should not be null");
343
validateConfigurationValue(key, value);
344
}
345
));
346
}
347
348
@TestFactory
349
Stream<DynamicNode> testConfigurationGroups() throws IOException {
350
Map<String, Properties> configGroups = loadConfigurationGroups();
351
352
return configGroups.entrySet().stream()
353
.map(group -> DynamicContainer.dynamicContainer(
354
"Config Group: " + group.getKey(),
355
group.getValue().entrySet().stream()
356
.map(config -> DynamicTest.dynamicTest(
357
"Test " + config.getKey(),
358
() -> validateConfigurationValue(
359
(String) config.getKey(),
360
(String) config.getValue()
361
)
362
))
363
));
364
}
365
366
private Properties loadTestConfigurations() throws IOException {
367
Properties props = new Properties();
368
props.load(getClass().getResourceAsStream("/test-config.properties"));
369
return props;
370
}
371
372
private Map<String, Properties> loadConfigurationGroups() {
373
// Load different configuration groups
374
return Map.of(
375
"database", loadDatabaseConfigs(),
376
"security", loadSecurityConfigs(),
377
"performance", loadPerformanceConfigs()
378
);
379
}
380
381
private void validateConfigurationValue(String key, String value) {
382
switch (key) {
383
case "timeout":
384
assertTrue(Integer.parseInt(value) > 0);
385
break;
386
case "url":
387
assertTrue(value.startsWith("http"));
388
break;
389
default:
390
assertNotNull(value);
391
}
392
}
393
}
394
```