skip to content

@Disabled

Turning tests off without deleting them, and why a bare @Disabled with no reason is a smell. Interviewers probe the difference between hard disabling and condition-based skipping.

on this pageshow

questions

5

In JUnit 5, what does the @Disabled annotation do, how does putting it on a test class differ from putting it on a single test method, and what is the string argument used for?

level: juniorimportance: must knowfreq 50%

answer

  1. skipped, not passed, not failed
  2. method = that test; class = whole container incl. @Nested
  3. optional reason string shows in reports
  4. no @Enabled to override inside a disabled class
  5. JUnit 4 @Ignore is the analogue — different package

basics

~20 s

@Disabled skips a test instead of running it, reporting it as skipped rather than failed. On a method it skips that method; on a class it skips every test in the class, including its nested classes. The string argument is the reason, shown in reports and IDEs.

solid answer

~50 s

`@Disabled` tells JUnit Jupiter not to execute something. The result is reported as **skipped**, not passed and not failed, so it does not turn the build red and it does not prove anything. - On a **method**, only that method is skipped; the rest of the class runs normally. - On a **class**, the whole container is skipped: every `@Test` in it and every `@Nested` class inside it. The class is not even instantiated. The optional `String` value is the reason, and it is surfaced in the console, the IDE and the XML report. Without it JUnit prints a generic default like *"void checkout() is @Disabled"*, which tells a future reader nothing. So the practical rule is: always pass a reason, and make it actionable — what is broken and where to follow it up, such as `@Disabled("flaky under parallel execution — see PROJ-1421")`. A skipped test with no explanation is indistinguishable from an abandoned one.

code

java · 15 lines
java
class CheckoutTest {

    @Test
    void chargesCard() { /* runs */ }

    @Test
    @Disabled("refund API not implemented yet - PROJ-88")
    void refundsCard() { /* skipped */ }
}

@Disabled("whole flow blocked by broken sandbox - PROJ-91")
class LegacyPaymentTest {
    @Test void a() { /* skipped */ }
    @Nested class Inner { @Test void b() { /* skipped too */ } }
}

go deeper

for a junior

State that it skips the test, that class level skips everything inside, and that the string is a reason shown in reports.

for a middle

Add that skipped is a distinct outcome from passed, and that reasons should carry a ticket reference.

for a senior

Emphasise that disabling is a deliberate, tracked loss of signal, and that conditional situations belong in conditional annotations instead.

for a principal

Talk about disabled tests as an inventory the team owns: expiry expectations, visibility in reporting, and the difference between blocked, flaky and obsolete.

## What it is `org.junit.jupiter.api.Disabled` is JUnit 5's unconditional "do not run this" marker — the successor to JUnit 4's `@Ignore`. It can be placed on a test method or on a test class, and it takes one optional `String` attribute: the reason. ## Method level versus class level On a **method**, the method is not executed. Everything else in the class behaves normally: other tests run, and the class-level setup still happens for them. On a **class**, the entire container is disabled. No test in it runs, no `@Nested` class inside it runs, and the class is never instantiated. This is the sharper tool: it removes a whole area of the suite from the signal, which is why class-level disabling deserves more scrutiny in review than a single method. A detail worth knowing: JUnit does not "inherit-cancel" the annotation. There is no `@Enabled` to re-enable one method inside a disabled class — if you need one test of ten to run, disable the other nine, or restructure. ## Reporting A disabled test appears in the results as **skipped**. Consequences: - the build stays green, so nothing forces anyone to notice; - test counts still include it in the total, with a separate skipped count; - IDEs and CI show the reason string next to it, if you supplied one. Extensions can observe it too: an extension implementing `TestWatcher` receives a `testDisabled(context, reason)` callback, which is how teams build reports that list skipped tests and their reasons. If you supply no reason, JUnit generates a default message naming the disabled element. That satisfies the tooling but not the next engineer. ## How to use it well - **Always give a reason, with a ticket.** `@Disabled("awaiting refund API — PROJ-88")` is reviewable; `@Disabled` alone becomes permanent by accident. - **Disable, do not comment out or delete the body.** A commented-out test is invisible to reporting; a disabled one is counted and can be found. - **Do not use it for environment differences.** "Only runs on Linux" or "needs a database URL" is a *conditional* situation; JUnit has built-in conditional annotations for that, and using `@Disabled` throws away the case where the test could have run. - **Never leave it as a way to hide a failure you do not understand.** Skipped is not passed; a disabled assertion is coverage you have silently lost. ## Relationship to JUnit 4 `@Ignore` in JUnit 4 is the direct analogue, also with an optional reason. Migrating is essentially a rename, but note the package: `org.junit.jupiter.api.Disabled`. A stray `org.junit.Ignore` on a Jupiter test does nothing at all — the Jupiter engine does not look for it — which is a classic silent-mistake during migration.

  • Why prefer @Disabled over commenting out the test body or deleting the method?
    A disabled test is still discovered and counted, appears in reports as skipped with its reason, and stays visible to anyone auditing coverage. Commented-out code is invisible to tooling and rots without anyone noticing. Deleting is legitimate when the behaviour genuinely no longer exists — but then delete deliberately, not as a way to silence a failure.
  • A JUnit 5 test still runs despite being annotated @Ignore. Why?
    `@Ignore` is JUnit 4's annotation in `org.junit`; the Jupiter engine does not look for it, so it is simply an unknown annotation and the test executes. The Jupiter equivalent is `org.junit.jupiter.api.Disabled`. Wrong-package imports of this kind are a common and silent migration bug.

saying these in an interview costs you the question

  • Saying a disabled test is reported as passing
  • Believing one method inside a @Disabled class can be re-enabled with another annotation
  • Using @Disabled for environment- or OS-dependent tests instead of a conditional annotation
  • Leaving @Disabled with no reason and treating that as acceptable
  • Thinking @Disabled removes the test from the total count entirely

context

open as a page

If a JUnit 5 test method is annotated @Disabled, which lifecycle callbacks still run — @BeforeAll, @BeforeEach, @AfterEach, @AfterAll — and is the test class instantiated? How does the answer change when the whole class is disabled?

level: middleimportance: should knowfreq 30%

basics

~20 s

For a disabled method the class container still runs, so @BeforeAll and @AfterAll execute, but no instance is created for that method and its @BeforeEach/@AfterEach are skipped. For a disabled class nothing runs at all — no @BeforeAll, no @AfterAll, no instantiation.

open as a page

A JUnit 5 test only makes sense on Linux and only when the system property db.url is set. Compare marking it unconditionally @Disabled with using JUnit 5's conditional annotations such as @EnabledOnOs and @EnabledIfSystemProperty — which do you choose, and why does it matter?

level: middleimportance: should knowfreq 28%

basics

~20 s

Use the conditional annotations. @Disabled is unconditional: the test never runs anywhere, so you lose the signal even on machines where it would work. @EnabledOnOs(LINUX) and @EnabledIfSystemProperty run it exactly where its prerequisites hold and report it as skipped elsewhere, with the condition as the reason.

open as a page

How does JUnit 5 decide at runtime that a test is disabled, and how would you implement your own rule — for example skipping tests that need an external service when that service is unreachable? Is there a way to force such skipped tests to run anyway?

level: seniorimportance: should knowfreq 22%

basics

~20 s

Jupiter asks every registered ExecutionCondition extension before running a node; @Disabled is itself the built-in DisabledCondition. You implement ExecutionCondition, return ConditionEvaluationResult.disabled(reason) or enabled(reason), and register it with @ExtendWith. The config parameter junit.jupiter.conditions.deactivate switches conditions off by pattern so disabled tests run.

open as a page

A test suite contains forty methods annotated @Disabled, some of them for more than a year. How would you handle disabled tests as a matter of engineering policy?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Treat disabled tests as tracked debt: every one needs a reason plus a ticket, they are inventoried and reported, and each is triaged into fix, delete, or convert to a condition. Environment-dependent ones should never be @Disabled. Make skip counts visible so green builds stop hiding lost coverage.

open as a page