skip to content

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%

answer

  1. skips never fail the build — that is why they pile up
  2. triage: blocked / flaky / obsolete / environment-dependent
  3. environment-dependent belongs in a condition, not @Disabled
  4. reason + ticket mandatory; TestWatcher inventory; watch the trend
  5. periodic audit run with conditions deactivated

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.

solid answer

~60 s

Forty long-lived skips means the suite is quietly reporting less than it appears to. I would do three things. **Inventory and triage.** Every disabled test is one of: *blocked* (feature unbuilt — keep, with a ticket), *flaky* (the real defect is the test or the system — fix or delete, do not park), *obsolete* (behaviour is gone — delete), or *environment-dependent* (should be a conditional annotation or a custom `ExecutionCondition`, not `@Disabled`). Only the first category deserves to stay. **Make it visible.** Skipped tests do not fail builds, which is why they accumulate. Report the skip count and the reasons — a `TestWatcher` extension collecting `testDisabled` gives you a list — and periodically run an audit with `junit.jupiter.conditions.deactivate` set so you see what actually still breaks. **Set rules going forward.** No `@Disabled` without a reason and a ticket; disabling in a PR is a review topic, not a formality; a skip that outlives its ticket gets deleted rather than inherited. Deleting is a legitimate outcome. A test nobody will fix is worth less than the honesty of removing it.

go deeper

for a junior

Say that disabled tests need a reason and a ticket and should not be left forever.

for a middle

Add the triage categories and the point that environment-dependent tests belong in conditional annotations.

for a senior

Bring operational mechanics: skip reporting via TestWatcher, an audit run with conditions deactivated, and trend monitoring.

for a principal

Argue from the principle that the suite must not overstate what it verifies, weigh the failure modes of stricter policies, and be willing to delete tests as a deliberate, recorded decision.

## Why disabled tests accumulate A skipped test is not a failure, so nothing in the pipeline pushes back. The person who disables it intends to come back; the person who inherits it does not know the story. Over a year, forty of them mean the green build overstates coverage by an unknown amount, and nobody can say by how much. The second-order harm is cultural: once disabling is the accepted response to a failing test, the suite stops being a gate and becomes a suggestion. ## Triage categories Every disabled test falls into one of four buckets, and the right action differs: 1. **Blocked** — the feature is not implemented, or an upstream dependency is not ready. Legitimate. Keep it, with a reason string naming the ticket, and expect it to disappear when the ticket closes. 2. **Flaky** — it passes sometimes. Disabling parks a real defect, in the test or in the system, and often in production behaviour. Either fix it, or delete it and record the gap. Parking indefinitely is the worst option because it preserves the illusion that the case is covered. 3. **Obsolete** — the behaviour no longer exists or was rewritten. Delete. Version control keeps the history; a skipped test does not. 4. **Environment-dependent** — it needs Linux, a licence key, a reachable sandbox. This is a misuse of `@Disabled`: it should be a conditional annotation or a custom `ExecutionCondition`, so it runs wherever the prerequisites hold and self-documents why it was skipped elsewhere. In my experience most long-lived skips are categories 2 and 4 wearing category 1's clothes. ## Making the debt visible - **Require a reason with a ticket.** `@Disabled("PROJ-1421: flaky under parallel execution")`. A reason-less `@Disabled` should not pass review. - **Report the inventory.** An extension implementing `TestWatcher` collects `testDisabled(context, reason)` and writes a list; publish it with the build so the number is seen, not merely available. - **Watch the trend, not the absolute.** A slowly rising skip count is the signal; the exact number matters less than its direction. - **Run a periodic audit** with conditions deactivated (`junit.jupiter.conditions.deactivate`) in a non-gating job. Tests that now pass get re-enabled; tests that fail get a fresh, accurate reason. This converts stale annotations into current information without anyone editing forty files by hand. - **Consider an expiry convention.** Some teams add a date to the reason and fail the audit job past it. Useful if the team will honour it; theatre if not. ## Where I would not go - **Blanket-deleting all forty** loses genuinely blocked cases that will come back. - **Failing the main build on any skip** sounds principled but drives people to comment tests out or delete them quietly, which is strictly worse: at least a skip is countable. - **Automatic retry as a flakiness cure.** It converts a visible skip into an invisible instability and delays the real diagnosis. ## The judgement call The underlying principle is that the suite must not lie. A test is either an assertion the team stands behind, or it should not be pretending to exist. `@Disabled` is a legitimate, temporary, *annotated* exception to that — with a reason, an owner and an expected end. Everything else is either fixed or removed.

  • Someone argues that deleting a disabled test loses coverage. How do you respond?
    A disabled test provides zero coverage today — it is a note, not a check. If nobody will fix it, keeping it only makes the suite look more thorough than it is. Delete it and, if the risk is real, record it as a gap in the backlog so the decision is explicit rather than hidden behind an annotation.
  • How would you stop new long-lived disables from appearing?
    Make disabling visible at the moment it happens: require a reason with a ticket, treat adding @Disabled as a reviewable change rather than a formality, and publish the skip inventory with every build so the count is a number the team sees. A periodic audit run with conditions deactivated then keeps the existing entries honest instead of letting them ossify.

saying these in an interview costs you the question

  • Treating skipped as equivalent to passing when reporting suite health
  • Parking flaky tests with @Disabled indefinitely instead of fixing or deleting them
  • Using @Disabled for environment-dependent tests rather than a conditional annotation
  • Proposing to fail the build on any skip without considering that it pushes people to delete tests quietly
  • Adding automatic retries as the answer to flakiness

context