JUnit Jupiter API for writing tests - Core API module of JUnit 5 that provides annotations, assertions, and test lifecycle management
—
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.
Enable or disable tests based on the current operating system and architecture.
/**
* Enable test only on specified operating systems
*/
@EnabledOnOs(OS value)
@EnabledOnOs(OS[] value)
@EnabledOnOs(value = OS.LINUX, architectures = "aarch64")
@EnabledOnOs(value = {OS.LINUX, OS.MAC}, disabledReason = "Not supported on Windows")
/**
* Disable test on specified operating systems
*/
@DisabledOnOs(OS value)
@DisabledOnOs(OS[] value)
@DisabledOnOs(value = OS.WINDOWS, architectures = {"x86_64", "amd64"})
/**
* Operating system enumeration
*/
enum OS {
AIX, FREEBSD, LINUX, MAC, OPENBSD, SOLARIS, WINDOWS, OTHER;
/**
* Get the current operating system
*/
static OS current();
/**
* Check if this OS matches the current system
*/
boolean isCurrentOs();
}Usage Examples:
import org.junit.jupiter.api.condition.*;
class PlatformSpecificTest {
@Test
@EnabledOnOs(OS.LINUX)
void linuxOnlyTest() {
// This test only runs on Linux
assertTrue(System.getProperty("os.name").toLowerCase().contains("linux"));
}
@Test
@DisabledOnOs({OS.WINDOWS, OS.MAC})
void notOnWindowsOrMac() {
// Runs on all systems except Windows and Mac
ProcessBuilder pb = new ProcessBuilder("ps", "-ef");
assertDoesNotThrow(() -> pb.start());
}
@Test
@EnabledOnOs(value = OS.LINUX, architectures = "aarch64")
void linuxArm64Only() {
// Only runs on ARM64 Linux systems
assertEquals("aarch64", System.getProperty("os.arch"));
}
@Test
@DisabledOnOs(value = OS.WINDOWS, disabledReason = "File permissions work differently on Windows")
void unixFilePermissions() {
// Test Unix-specific file permission behavior
Path file = Files.createFile(tempDir.resolve("test.txt"));
Files.setPosixFilePermissions(file, PosixFilePermissions.fromString("rwxr--r--"));
}
}Enable or disable tests based on JRE version.
/**
* Enable test only on specified JRE versions
*/
@EnabledOnJre(JRE value)
@EnabledOnJre(JRE[] value)
/**
* Disable test on specified JRE versions
*/
@DisabledOnJre(JRE value)
@DisabledOnJre(JRE[] value)
/**
* Enable test for a range of JRE versions
*/
@EnabledForJreRange(JRE min = JRE.JAVA_8, JRE max = JRE.JAVA_17)
/**
* Disable test for a range of JRE versions
*/
@DisabledForJreRange(JRE min = JRE.JAVA_8, JRE max = JRE.JAVA_11)
/**
* Java Runtime Environment version enumeration
*/
enum JRE {
JAVA_8, JAVA_9, JAVA_10, JAVA_11, JAVA_12, JAVA_13, JAVA_14,
JAVA_15, JAVA_16, JAVA_17, JAVA_18, JAVA_19, JAVA_20, JAVA_21, OTHER;
/**
* Get the current JRE version
*/
static JRE current();
/**
* Check if this JRE version matches the current runtime
*/
boolean isCurrentVersion();
}Usage Examples:
class JreSpecificTest {
@Test
@EnabledOnJre(JRE.JAVA_11)
void java11OnlyFeature() {
// Test features specific to Java 11
String result = "hello world".repeat(3);
assertEquals("hello worldhello worldhello world", result);
}
@Test
@DisabledOnJre({JRE.JAVA_8, JRE.JAVA_9})
void modernJavaFeatures() {
// Uses features not available in Java 8/9
var list = List.of("item1", "item2", "item3");
assertTrue(list.size() > 0);
}
@Test
@EnabledForJreRange(min = JRE.JAVA_11, max = JRE.JAVA_17)
void supportedJavaVersions() {
// Test runs on Java 11 through 17
assertDoesNotThrow(() -> {
// Use features available in this range
String text = """
Multi-line
string literal
""";
assertFalse(text.isEmpty());
});
}
@Test
@DisabledForJreRange(min = JRE.JAVA_8, max = JRE.JAVA_10, disabledReason = "Module system required")
void moduleSystemFeatures() {
// Test module system features not available before Java 11
Module module = getClass().getModule();
assertNotNull(module);
}
}Enable or disable tests based on system properties.
/**
* Enable test if system property matches condition
*/
@EnabledIfSystemProperty(named = "property.name", matches = "regex")
/**
* Disable test if system property matches condition
*/
@DisabledIfSystemProperty(named = "property.name", matches = "regex")
/**
* Multiple system property conditions (all must be true)
*/
@EnabledIfSystemProperties({
@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*"),
@EnabledIfSystemProperty(named = "java.vendor", matches = ".*OpenJDK.*")
})
@DisabledIfSystemProperties({
@DisabledIfSystemProperty(named = "env.type", matches = "production"),
@DisabledIfSystemProperty(named = "skip.integration", matches = "true")
})Usage Examples:
class SystemPropertyTest {
@Test
@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")
void on64BitArchitecture() {
// Only runs on 64-bit architectures
String arch = System.getProperty("os.arch");
assertTrue(arch.contains("64"));
}
@Test
@DisabledIfSystemProperty(named = "headless", matches = "true")
void guiTest() {
// Skipped in headless environments
assertFalse(GraphicsEnvironment.isHeadless());
// GUI testing code here
}
@Test
@EnabledIfSystemProperties({
@EnabledIfSystemProperty(named = "test.environment", matches = "integration"),
@EnabledIfSystemProperty(named = "database.url", matches = ".*localhost.*")
})
void integrationTestWithLocalDatabase() {
// Only runs if both conditions are met
assertEquals("integration", System.getProperty("test.environment"));
assertTrue(System.getProperty("database.url").contains("localhost"));
}
@Test
@DisabledIfSystemProperty(named = "skip.slow.tests", matches = "true", disabledReason = "Slow tests disabled")
void slowIntegrationTest() {
// Skip when -Dskip.slow.tests=true is set
Thread.sleep(5000); // Simulate slow test
}
}Enable or disable tests based on environment variables.
/**
* Enable test if environment variable matches condition
*/
@EnabledIfEnvironmentVariable(named = "ENV_VAR", matches = "regex")
/**
* Disable test if environment variable matches condition
*/
@DisabledIfEnvironmentVariable(named = "ENV_VAR", matches = "regex")
/**
* Multiple environment variable conditions (all must be true)
*/
@EnabledIfEnvironmentVariables({
@EnabledIfEnvironmentVariable(named = "CI", matches = "true"),
@EnabledIfEnvironmentVariable(named = "BUILD_ENV", matches = "staging|production")
})
@DisabledIfEnvironmentVariables({
@DisabledIfEnvironmentVariable(named = "SKIP_TESTS", matches = ".*integration.*"),
@DisabledIfEnvironmentVariable(named = "LIGHTWEIGHT_BUILD", matches = "true")
})Usage Examples:
class EnvironmentVariableTest {
@Test
@EnabledIfEnvironmentVariable(named = "CI", matches = "true")
void continuousIntegrationTest() {
// Only runs in CI environments
assertEquals("true", System.getenv("CI"));
// CI-specific test logic
}
@Test
@DisabledIfEnvironmentVariable(named = "SKIP_DOCKER_TESTS", matches = "true")
void dockerContainerTest() {
// Skipped when SKIP_DOCKER_TESTS=true
// Docker-based integration test
DockerClient client = DockerClient.getInstance();
assertNotNull(client);
}
@Test
@EnabledIfEnvironmentVariables({
@EnabledIfEnvironmentVariable(named = "AWS_REGION", matches = "us-.*"),
@EnabledIfEnvironmentVariable(named = "AWS_ACCESS_KEY_ID", matches = ".+")
})
void awsIntegrationTest() {
// Only runs with proper AWS configuration in US regions
String region = System.getenv("AWS_REGION");
assertTrue(region.startsWith("us-"));
assertNotNull(System.getenv("AWS_ACCESS_KEY_ID"));
}
@Test
@DisabledIfEnvironmentVariable(named = "BUILD_ENV", matches = "production", disabledReason = "Not safe in production")
void destructiveTest() {
// Never runs in production environment
// Potentially destructive test operations
}
}Enable or disable tests based on custom condition methods.
/**
* Enable test if method returns true
*/
@EnabledIf("methodName")
@EnabledIf(value = "methodName", disabledReason = "Custom condition not met")
/**
* Disable test if method returns true
*/
@DisabledIf("methodName")
@DisabledIf(value = "methodName", disabledReason = "Custom condition prevents execution")Usage Examples:
class CustomConditionTest {
@Test
@EnabledIf("isDatabaseAvailable")
void databaseTest() {
// Only runs if database is available
assertTrue(isDatabaseAvailable());
// Database test logic
}
@Test
@DisabledIf("isProductionEnvironment")
void developmentOnlyTest() {
// Skipped in production
assertFalse(isProductionEnvironment());
// Development/testing specific code
}
@Test
@EnabledIf(value = "hasRequiredFeature", disabledReason = "Required feature not available")
void featureSpecificTest() {
// Only runs if specific feature is available
assertTrue(hasRequiredFeature());
// Feature-specific test logic
}
// Condition methods must be static and return boolean
static boolean isDatabaseAvailable() {
try {
java.sql.Connection conn = java.sql.DriverManager.getConnection("jdbc:h2:mem:test");
conn.close();
return true;
} catch (java.sql.SQLException e) {
return false;
}
}
static boolean isProductionEnvironment() {
return "production".equals(System.getenv("ENV"));
}
static boolean hasRequiredFeature() {
return System.getProperty("features", "").contains("advanced");
}
}Enable or disable tests when running in GraalVM native image.
/**
* Enable test only when running in a native image
*/
@EnabledInNativeImage
/**
* Disable test when running in a native image
*/
@DisabledInNativeImageUsage Examples:
class NativeImageTest {
@Test
@EnabledInNativeImage
void nativeImageOnlyTest() {
// Only runs in GraalVM native image
// Test native image specific behavior
assertTrue(isNativeImage());
}
@Test
@DisabledInNativeImage
void jvmOnlyTest() {
// Skipped in native image, uses reflection heavily
Class<?> clazz = SomeClass.class;
Method[] methods = clazz.getDeclaredMethods();
assertTrue(methods.length > 0);
}
private boolean isNativeImage() {
return System.getProperty("org.graalvm.nativeimage.imagecode") != null;
}
}Multiple conditional annotations can be combined on the same test method.
Usage Examples:
class CombinedConditionsTest {
@Test
@EnabledOnOs(OS.LINUX)
@EnabledForJreRange(min = JRE.JAVA_11)
@EnabledIfEnvironmentVariable(named = "CI", matches = "true")
void linuxJava11CiTest() {
// Only runs on Linux, Java 11+, in CI environment
// All conditions must be satisfied
}
@Test
@DisabledOnOs(OS.WINDOWS)
@DisabledIfSystemProperty(named = "headless", matches = "true")
@DisabledIf("isResourceConstrained")
void resourceIntensiveTest() {
// Skipped if ANY condition is met:
// - Running on Windows, OR
// - In headless mode, OR
// - System is resource constrained
}
static boolean isResourceConstrained() {
Runtime runtime = Runtime.getRuntime();
return runtime.maxMemory() < 1024 * 1024 * 1024; // Less than 1GB
}
}When conditional annotations are misconfigured or condition methods throw exceptions, JUnit will report the error and typically disable the affected test.
@Test
@EnabledIf("nonExistentMethod") // Error: method not found
void misconfiguredTest() {
// This test will be disabled due to configuration error
}
@Test
@EnabledIf("throwingConditionMethod")
void testWithThrowingCondition() {
// This test will be disabled if condition method throws exception
}
static boolean throwingConditionMethod() {
throw new RuntimeException("Condition evaluation failed");
}/**
* Result of evaluating an execution condition
*/
class ConditionEvaluationResult {
static ConditionEvaluationResult enabled(String reason);
static ConditionEvaluationResult disabled(String reason);
boolean isDisabled();
Optional<String> getReason();
}Install with Tessl CLI
npx tessl i tessl/maven-org-junit-jupiter--junit-jupiter-api