JUnit 5 provides several ways to conditionally execute tests based on various runtime conditions. This is useful when tests should only run under specific circumstances — like a particular operating system, JRE version, environment variable, or system property.
1. Operating System Conditions
Use @EnabledOnOs and @DisabledOnOs to run tests only on specific operating systems.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
class OsConditionalTest {
@Test
@EnabledOnOs(OS.WINDOWS)
void onlyOnWindows() {
// Runs only on Windows
}
@Test
@EnabledOnOs({OS.LINUX, OS.MAC})
void onlyOnLinuxOrMac() {
// Runs only on Linux or macOS
}
@Test
@DisabledOnOs(OS.WINDOWS)
void notOnWindows() {
// Skipped on Windows
}
}
2. JRE Version Conditions
Use @EnabledOnJre, @DisabledOnJre, or @EnabledForJreRange to control tests based on the Java version.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnJre;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
class JreConditionalTest {
@Test
@EnabledOnJre(JRE.JAVA_21)
void onlyOnJava21() {
// Runs only on Java 21
}
@Test
@EnabledForJreRange(min = JRE.JAVA_17, max = JRE.JAVA_25)
void betweenJava17AndJava25() {
// Runs on Java 17 through 25
}
}
3. System Property Conditions
Enable or disable tests based on JVM system properties.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
class SystemPropertyTest {
@Test
@EnabledIfSystemProperty(named = "env", matches = "ci")
void onlyInCiEnvironment() {
// Runs only when -Denv=ci
}
@Test
@DisabledIfSystemProperty(named = "os.arch", matches = ".*32.*")
void notOn32BitArch() {
// Skipped on 32-bit architectures
}
}
4. Environment Variable Conditions
Similar to system properties, but for OS-level environment variables.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
class EnvVarTest {
@Test
@EnabledIfEnvironmentVariable(named = "CI", matches = "true")
void onlyInCi() {
// Runs only when CI=true in environment
}
@Test
@DisabledIfEnvironmentVariable(named = "ENV", matches = "prod")
void skipInProduction() {
// Skipped when ENV=prod
}
}
5. Custom Conditions with @EnabledIf and @DisabledIf
For more complex logic, delegate to a static method that returns a boolean.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
class CustomConditionTest {
@Test
@EnabledIf("isDatabaseAvailable")
void runOnlyIfDatabaseIsUp() {
// Runs only if the referenced method returns true
}
static boolean isDatabaseAvailable() {
// Perform a real check (ping DB, check port, etc.)
return "true".equalsIgnoreCase(System.getenv("DB_UP"));
}
}
The method must:
- Be
static(unless the test class is@TestInstance(PER_CLASS)) - Return
boolean - Take no arguments (or accept
ExtensionContext)
6. Programmatic Conditions with Assumptions
Sometimes you don’t want a test skipped by annotation but rather aborted mid-execution based on runtime state. Use Assumptions:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.junit.jupiter.api.Assumptions.assumingThat;
class AssumptionTest {
@Test
void runOnlyIfOnDeveloperMachine() {
assumeTrue("DEV".equals(System.getenv("PROFILE")),
"Skipping: not on developer profile");
// rest of test logic
}
@Test
void partiallyConditional() {
assumingThat("CI".equals(System.getenv("PROFILE")), () -> {
// Only executed in CI, but the outer test still runs
});
// Always executed
}
}
Difference:
@Disabled*/@Enabled*→ skipped at discovery time (test shows as skipped).Assumptions→ aborted at execution time (test shows as aborted).
Which One Should You Use?
| Scenario | Recommended Approach |
|---|---|
| Skip based on OS / JRE | @EnabledOnOs, @EnabledOnJre |
| Skip based on env variable or system property | @EnabledIfEnvironmentVariable, @EnabledIfSystemProperty |
| Complex, dynamic condition | @EnabledIf("methodName") |
| Runtime state check inside the test | Assumptions.assumeTrue(...) |
Key Takeaways
- Use annotation-based conditions for static, predictable rules (OS, JRE, env).
- Use
@EnabledIf/@DisabledIffor custom logic that can’t be expressed with the built-in annotations. - Use
Assumptionswhen you need to abort a test at runtime based on data available only during execution. - Always provide a reason/message (e.g.,
disabledReason = "...") — future you (and your teammates) will thank you when reading test reports.
