0
# Conditional Test Execution
1
2
Runtime environment-based test control for platform-specific, JRE-specific, and custom conditional testing scenarios. These annotations allow tests to be enabled or disabled based on operating system, Java version, system properties, environment variables, or custom conditions.
3
4
## Capabilities
5
6
### Operating System Conditions
7
8
Enable or disable tests based on the current operating system and architecture.
9
10
```java { .api }
11
/**
12
* Enable test only on specified operating systems
13
*/
14
@EnabledOnOs(OS value)
15
@EnabledOnOs(OS[] value)
16
@EnabledOnOs(value = OS.LINUX, architectures = "aarch64")
17
@EnabledOnOs(value = {OS.LINUX, OS.MAC}, disabledReason = "Not supported on Windows")
18
19
/**
20
* Disable test on specified operating systems
21
*/
22
@DisabledOnOs(OS value)
23
@DisabledOnOs(OS[] value)
24
@DisabledOnOs(value = OS.WINDOWS, architectures = {"x86_64", "amd64"})
25
26
/**
27
* Operating system enumeration
28
*/
29
enum OS {
30
AIX, FREEBSD, LINUX, MAC, OPENBSD, SOLARIS, WINDOWS, OTHER;
31
32
/**
33
* Get the current operating system
34
*/
35
static OS current();
36
37
/**
38
* Check if this OS matches the current system
39
*/
40
boolean isCurrentOs();
41
}
42
```
43
44
**Usage Examples:**
45
46
```java
47
import org.junit.jupiter.api.condition.*;
48
49
class PlatformSpecificTest {
50
51
@Test
52
@EnabledOnOs(OS.LINUX)
53
void linuxOnlyTest() {
54
// This test only runs on Linux
55
assertTrue(System.getProperty("os.name").toLowerCase().contains("linux"));
56
}
57
58
@Test
59
@DisabledOnOs({OS.WINDOWS, OS.MAC})
60
void notOnWindowsOrMac() {
61
// Runs on all systems except Windows and Mac
62
ProcessBuilder pb = new ProcessBuilder("ps", "-ef");
63
assertDoesNotThrow(() -> pb.start());
64
}
65
66
@Test
67
@EnabledOnOs(value = OS.LINUX, architectures = "aarch64")
68
void linuxArm64Only() {
69
// Only runs on ARM64 Linux systems
70
assertEquals("aarch64", System.getProperty("os.arch"));
71
}
72
73
@Test
74
@DisabledOnOs(value = OS.WINDOWS, disabledReason = "File permissions work differently on Windows")
75
void unixFilePermissions() {
76
// Test Unix-specific file permission behavior
77
Path file = Files.createFile(tempDir.resolve("test.txt"));
78
Files.setPosixFilePermissions(file, PosixFilePermissions.fromString("rwxr--r--"));
79
}
80
}
81
```
82
83
### Java Runtime Environment Conditions
84
85
Enable or disable tests based on JRE version.
86
87
```java { .api }
88
/**
89
* Enable test only on specified JRE versions
90
*/
91
@EnabledOnJre(JRE value)
92
@EnabledOnJre(JRE[] value)
93
94
/**
95
* Disable test on specified JRE versions
96
*/
97
@DisabledOnJre(JRE value)
98
@DisabledOnJre(JRE[] value)
99
100
/**
101
* Enable test for a range of JRE versions
102
*/
103
@EnabledForJreRange(JRE min = JRE.JAVA_8, JRE max = JRE.JAVA_17)
104
105
/**
106
* Disable test for a range of JRE versions
107
*/
108
@DisabledForJreRange(JRE min = JRE.JAVA_8, JRE max = JRE.JAVA_11)
109
110
/**
111
* Java Runtime Environment version enumeration
112
*/
113
enum JRE {
114
JAVA_8, JAVA_9, JAVA_10, JAVA_11, JAVA_12, JAVA_13, JAVA_14,
115
JAVA_15, JAVA_16, JAVA_17, JAVA_18, JAVA_19, JAVA_20, JAVA_21, OTHER;
116
117
/**
118
* Get the current JRE version
119
*/
120
static JRE current();
121
122
/**
123
* Check if this JRE version matches the current runtime
124
*/
125
boolean isCurrentVersion();
126
}
127
```
128
129
**Usage Examples:**
130
131
```java
132
class JreSpecificTest {
133
134
@Test
135
@EnabledOnJre(JRE.JAVA_11)
136
void java11OnlyFeature() {
137
// Test features specific to Java 11
138
String result = "hello world".repeat(3);
139
assertEquals("hello worldhello worldhello world", result);
140
}
141
142
@Test
143
@DisabledOnJre({JRE.JAVA_8, JRE.JAVA_9})
144
void modernJavaFeatures() {
145
// Uses features not available in Java 8/9
146
var list = List.of("item1", "item2", "item3");
147
assertTrue(list.size() > 0);
148
}
149
150
@Test
151
@EnabledForJreRange(min = JRE.JAVA_11, max = JRE.JAVA_17)
152
void supportedJavaVersions() {
153
// Test runs on Java 11 through 17
154
assertDoesNotThrow(() -> {
155
// Use features available in this range
156
String text = """
157
Multi-line
158
string literal
159
""";
160
assertFalse(text.isEmpty());
161
});
162
}
163
164
@Test
165
@DisabledForJreRange(min = JRE.JAVA_8, max = JRE.JAVA_10, disabledReason = "Module system required")
166
void moduleSystemFeatures() {
167
// Test module system features not available before Java 11
168
Module module = getClass().getModule();
169
assertNotNull(module);
170
}
171
}
172
```
173
174
### System Property Conditions
175
176
Enable or disable tests based on system properties.
177
178
```java { .api }
179
/**
180
* Enable test if system property matches condition
181
*/
182
@EnabledIfSystemProperty(named = "property.name", matches = "regex")
183
184
/**
185
* Disable test if system property matches condition
186
*/
187
@DisabledIfSystemProperty(named = "property.name", matches = "regex")
188
189
/**
190
* Multiple system property conditions (all must be true)
191
*/
192
@EnabledIfSystemProperties({
193
@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*"),
194
@EnabledIfSystemProperty(named = "java.vendor", matches = ".*OpenJDK.*")
195
})
196
197
@DisabledIfSystemProperties({
198
@DisabledIfSystemProperty(named = "env.type", matches = "production"),
199
@DisabledIfSystemProperty(named = "skip.integration", matches = "true")
200
})
201
```
202
203
**Usage Examples:**
204
205
```java
206
class SystemPropertyTest {
207
208
@Test
209
@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")
210
void on64BitArchitecture() {
211
// Only runs on 64-bit architectures
212
String arch = System.getProperty("os.arch");
213
assertTrue(arch.contains("64"));
214
}
215
216
@Test
217
@DisabledIfSystemProperty(named = "headless", matches = "true")
218
void guiTest() {
219
// Skipped in headless environments
220
assertFalse(GraphicsEnvironment.isHeadless());
221
// GUI testing code here
222
}
223
224
@Test
225
@EnabledIfSystemProperties({
226
@EnabledIfSystemProperty(named = "test.environment", matches = "integration"),
227
@EnabledIfSystemProperty(named = "database.url", matches = ".*localhost.*")
228
})
229
void integrationTestWithLocalDatabase() {
230
// Only runs if both conditions are met
231
assertEquals("integration", System.getProperty("test.environment"));
232
assertTrue(System.getProperty("database.url").contains("localhost"));
233
}
234
235
@Test
236
@DisabledIfSystemProperty(named = "skip.slow.tests", matches = "true", disabledReason = "Slow tests disabled")
237
void slowIntegrationTest() {
238
// Skip when -Dskip.slow.tests=true is set
239
Thread.sleep(5000); // Simulate slow test
240
}
241
}
242
```
243
244
### Environment Variable Conditions
245
246
Enable or disable tests based on environment variables.
247
248
```java { .api }
249
/**
250
* Enable test if environment variable matches condition
251
*/
252
@EnabledIfEnvironmentVariable(named = "ENV_VAR", matches = "regex")
253
254
/**
255
* Disable test if environment variable matches condition
256
*/
257
@DisabledIfEnvironmentVariable(named = "ENV_VAR", matches = "regex")
258
259
/**
260
* Multiple environment variable conditions (all must be true)
261
*/
262
@EnabledIfEnvironmentVariables({
263
@EnabledIfEnvironmentVariable(named = "CI", matches = "true"),
264
@EnabledIfEnvironmentVariable(named = "BUILD_ENV", matches = "staging|production")
265
})
266
267
@DisabledIfEnvironmentVariables({
268
@DisabledIfEnvironmentVariable(named = "SKIP_TESTS", matches = ".*integration.*"),
269
@DisabledIfEnvironmentVariable(named = "LIGHTWEIGHT_BUILD", matches = "true")
270
})
271
```
272
273
**Usage Examples:**
274
275
```java
276
class EnvironmentVariableTest {
277
278
@Test
279
@EnabledIfEnvironmentVariable(named = "CI", matches = "true")
280
void continuousIntegrationTest() {
281
// Only runs in CI environments
282
assertEquals("true", System.getenv("CI"));
283
// CI-specific test logic
284
}
285
286
@Test
287
@DisabledIfEnvironmentVariable(named = "SKIP_DOCKER_TESTS", matches = "true")
288
void dockerContainerTest() {
289
// Skipped when SKIP_DOCKER_TESTS=true
290
// Docker-based integration test
291
DockerClient client = DockerClient.getInstance();
292
assertNotNull(client);
293
}
294
295
@Test
296
@EnabledIfEnvironmentVariables({
297
@EnabledIfEnvironmentVariable(named = "AWS_REGION", matches = "us-.*"),
298
@EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".+")
299
})
300
void awsIntegrationTest() {
301
// Only runs with proper AWS configuration in US regions
302
String region = System.getenv("AWS_REGION");
303
assertTrue(region.startsWith("us-"));
304
assertNotNull(System.getenv("AWS_ACCESS_KEY_ID"));
305
}
306
307
@Test
308
@DisabledIfEnvironmentVariable(named = "BUILD_ENV", matches = "production", disabledReason = "Not safe in production")
309
void destructiveTest() {
310
// Never runs in production environment
311
// Potentially destructive test operations
312
}
313
}
314
```
315
316
### Custom Method Conditions
317
318
Enable or disable tests based on custom condition methods.
319
320
```java { .api }
321
/**
322
* Enable test if method returns true
323
*/
324
@EnabledIf("methodName")
325
@EnabledIf(value = "methodName", disabledReason = "Custom condition not met")
326
327
/**
328
* Disable test if method returns true
329
*/
330
@DisabledIf("methodName")
331
@DisabledIf(value = "methodName", disabledReason = "Custom condition prevents execution")
332
```
333
334
**Usage Examples:**
335
336
```java
337
class CustomConditionTest {
338
339
@Test
340
@EnabledIf("isDatabaseAvailable")
341
void databaseTest() {
342
// Only runs if database is available
343
assertTrue(isDatabaseAvailable());
344
// Database test logic
345
}
346
347
@Test
348
@DisabledIf("isProductionEnvironment")
349
void developmentOnlyTest() {
350
// Skipped in production
351
assertFalse(isProductionEnvironment());
352
// Development/testing specific code
353
}
354
355
@Test
356
@EnabledIf(value = "hasRequiredFeature", disabledReason = "Required feature not available")
357
void featureSpecificTest() {
358
// Only runs if specific feature is available
359
assertTrue(hasRequiredFeature());
360
// Feature-specific test logic
361
}
362
363
// Condition methods must be static and return boolean
364
static boolean isDatabaseAvailable() {
365
try {
366
java.sql.Connection conn = java.sql.DriverManager.getConnection("jdbc:h2:mem:test");
367
conn.close();
368
return true;
369
} catch (java.sql.SQLException e) {
370
return false;
371
}
372
}
373
374
static boolean isProductionEnvironment() {
375
return "production".equals(System.getenv("ENV"));
376
}
377
378
static boolean hasRequiredFeature() {
379
return System.getProperty("features", "").contains("advanced");
380
}
381
}
382
```
383
384
### Native Image Conditions
385
386
Enable or disable tests when running in GraalVM native image.
387
388
```java { .api }
389
/**
390
* Enable test only when running in a native image
391
*/
392
@EnabledInNativeImage
393
394
/**
395
* Disable test when running in a native image
396
*/
397
@DisabledInNativeImage
398
```
399
400
**Usage Examples:**
401
402
```java
403
class NativeImageTest {
404
405
@Test
406
@EnabledInNativeImage
407
void nativeImageOnlyTest() {
408
// Only runs in GraalVM native image
409
// Test native image specific behavior
410
assertTrue(isNativeImage());
411
}
412
413
@Test
414
@DisabledInNativeImage
415
void jvmOnlyTest() {
416
// Skipped in native image, uses reflection heavily
417
Class<?> clazz = SomeClass.class;
418
Method[] methods = clazz.getDeclaredMethods();
419
assertTrue(methods.length > 0);
420
}
421
422
private boolean isNativeImage() {
423
return System.getProperty("org.graalvm.nativeimage.imagecode") != null;
424
}
425
}
426
```
427
428
### Combining Conditions
429
430
Multiple conditional annotations can be combined on the same test method.
431
432
**Usage Examples:**
433
434
```java
435
class CombinedConditionsTest {
436
437
@Test
438
@EnabledOnOs(OS.LINUX)
439
@EnabledForJreRange(min = JRE.JAVA_11)
440
@EnabledIfEnvironmentVariable(named = "CI", matches = "true")
441
void linuxJava11CiTest() {
442
// Only runs on Linux, Java 11+, in CI environment
443
// All conditions must be satisfied
444
}
445
446
@Test
447
@DisabledOnOs(OS.WINDOWS)
448
@DisabledIfSystemProperty(named = "headless", matches = "true")
449
@DisabledIf("isResourceConstrained")
450
void resourceIntensiveTest() {
451
// Skipped if ANY condition is met:
452
// - Running on Windows, OR
453
// - In headless mode, OR
454
// - System is resource constrained
455
}
456
457
static boolean isResourceConstrained() {
458
Runtime runtime = Runtime.getRuntime();
459
return runtime.maxMemory() < 1024 * 1024 * 1024; // Less than 1GB
460
}
461
}
462
```
463
464
## Error Handling
465
466
When conditional annotations are misconfigured or condition methods throw exceptions, JUnit will report the error and typically disable the affected test.
467
468
```java
469
@Test
470
@EnabledIf("nonExistentMethod") // Error: method not found
471
void misconfiguredTest() {
472
// This test will be disabled due to configuration error
473
}
474
475
@Test
476
@EnabledIf("throwingConditionMethod")
477
void testWithThrowingCondition() {
478
// This test will be disabled if condition method throws exception
479
}
480
481
static boolean throwingConditionMethod() {
482
throw new RuntimeException("Condition evaluation failed");
483
}
484
```
485
486
## Types
487
488
### Condition Evaluation Result
489
490
```java { .api }
491
/**
492
* Result of evaluating an execution condition
493
*/
494
class ConditionEvaluationResult {
495
static ConditionEvaluationResult enabled(String reason);
496
static ConditionEvaluationResult disabled(String reason);
497
498
boolean isDisabled();
499
Optional<String> getReason();
500
}
501
```