skip to content

When should you use a conditional annotation like @DisabledOnOs or @EnabledOnOs instead of an unconditional @Disabled?

level: middleimportance: should knowfreq 55%

answer

  1. @Disabled = always off, human re-enables
  2. Conditionals decide at runtime, self-correct
  3. OS / JRE / system property / env var / @EnabledIf
  4. Environmental reason -> conditional; broken/flaky -> @Disabled
  5. org.junit.jupiter.api.condition package

basics

~20 s

Use @Disabled when a test is just broken or parked. Use conditional annotations like @EnabledOnOs(WINDOWS) or @DisabledOnOs(MAC) when the test should run only under a real condition - a specific OS, Java version, or environment - so it re-enables itself automatically when that condition holds.

solid answer

~40 s

@Disabled is unconditional - the test is always skipped until a human removes the annotation. Conditional annotations skip or run a test based on a runtime condition, evaluated each time. JUnit 5 ships several: @EnabledOnOs / @DisabledOnOs (operating system), @EnabledOnJre / @DisabledOnJre (Java version), @EnabledIfSystemProperty / @DisabledIfSystemProperty, @EnabledIfEnvironmentVariable / @DisabledIfEnvironmentVariable, and the general-purpose @EnabledIf / @DisabledIf that call a method returning a boolean. The rule of thumb: if the reason a test shouldn't run is a *fact about the environment* (this only works on Linux, needs Java 21, requires an INTEGRATION=true env var), use a conditional - it self-corrects when the environment changes and documents the actual requirement. Reserve @Disabled for 'this test is broken/flaky and a human will fix it', where there is no environmental condition to key off.

go deeper

for a junior

Knows conditional annotations exist (@EnabledOnOs/@DisabledOnOs etc.) and that they skip based on a condition rather than always.

for a middle

Picks @Disabled vs a conditional correctly: environmental reason -> conditional, broken/flaky -> @Disabled, and names the built-in conditional families.

for a senior

Explains the self-correcting benefit and the silent-permanent-skip failure mode, and reaches for @EnabledIf for custom predicates.

for a principal

Shapes conventions so environment requirements are expressed declaratively (conditionals) and @Disabled is reserved for tracked, time-boxed parks across the org's suites.

## Two kinds of 'don't run this test' JUnit 5 lets you suppress a test in two fundamentally different ways: 1. **Unconditional** - `@Disabled`. The test is *always* skipped. The only way it runs again is for a person to delete or comment out the annotation. There is no logic; it is a hard off-switch. 2. **Conditional** - a family of annotations that decide at runtime, *every time the suite runs*, whether the test should execute. The decision is based on some fact about the current environment. ## The conditional family All live in `org.junit.jupiter.api.condition`. Each comes as an `@Enabled...` / `@Disabled...` pair: - **`@EnabledOnOs` / `@DisabledOnOs`** - keyed on the operating system, using the `OS` enum (`OS.WINDOWS`, `OS.MAC`, `OS.LINUX`, ...). `@EnabledOnOs(OS.LINUX)` runs only on Linux; `@DisabledOnOs(OS.WINDOWS)` runs everywhere except Windows. - **`@EnabledOnJre` / `@DisabledOnJre`** - keyed on the Java runtime version (the `JRE` enum, e.g. `JRE.JAVA_17`), or a version range via `@EnabledForJreRange`. - **`@EnabledIfSystemProperty` / `@DisabledIfSystemProperty`** - keyed on a JVM system property (`-Dkey=value`); you give a `named` and a `matches` regex. - **`@EnabledIfEnvironmentVariable` / `@DisabledIfEnvironmentVariable`** - same idea for OS environment variables. - **`@EnabledIf` / `@DisabledIf`** - the general escape hatch: name a method that returns `boolean`; the test runs (or is disabled) based on its result. ```java import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.condition.OS; @Test @EnabledOnOs(OS.LINUX) void usesEpoll() { // only meaningful on Linux } ``` ## The decision rule Ask: *why* shouldn't this test run right now? - **Because of a fact about the environment** (OS, Java version, a system property, an env var, presence of a resource) -> use a **conditional**. It self-corrects: when you run on the right OS or set the right property, the test runs again automatically, and the annotation *documents the real requirement* for free. - **Because the test is broken, flaky, or waiting on a code fix** - there is no environmental signal to key off -> use **`@Disabled`** with a reason and a ticket. A human will re-enable it deliberately. ## Why this distinction matters Misusing `@Disabled` for an environmental reason creates **silent permanent skips**: someone disables a Windows-only test on their Mac and it stays off for everyone, even on Windows. A conditional `@EnabledOnOs(OS.WINDOWS)` would have kept it running where it *should* run. Conversely, contorting a conditional to permanently turn a broken test off is just obfuscation - `@Disabled` is clearer. ## Reporting Both unconditional and conditional skips are reported as **skipped**, and the conditional annotations contribute a reason describing the unmet condition (e.g. 'Disabled on OS: MAC'), so the output explains itself.

  • A test only works on Linux. Which annotation is better - @Disabled or @EnabledOnOs(OS.LINUX)?
    @EnabledOnOs(OS.LINUX). It documents the real requirement and runs the test automatically wherever it should, instead of being a static off-switch that could mistakenly stay disabled everywhere.
  • Which conditional annotation would you reach for when none of the built-in ones (OS/JRE/property/env) fit?
    @EnabledIf / @DisabledIf, which delegate to a boolean-returning method so you can express any custom condition.

saying these in an interview costs you the question

  • Using @Disabled for an environment-specific test, causing it to stay off everywhere
  • Believing conditional annotations are evaluated once at compile time (they run each execution)
  • Confusing @DisabledOnOs (skip on that OS) with @EnabledOnOs (run only on that OS)
  • Thinking conditional skips are reported differently from disabled (both show as 'skipped')

context