Ignored Test.
A test that is committed to the codebase but never runs because it has been skipped, disabled, or commented out, giving the appearance of coverage without actually verifying anything.
##Signs and Symptoms
A test exists in the suite but is permanently prevented from executing. It still counts as a "test" in the file, yet contributes zero verification. Watch for skip/ignore markers, x-prefixed blocks, empty bodies, and test code buried in comments — often paired with a vague excuse like "flaky" or "fix later".
// skip/disabled modifiers — never run
describe.skip('checkout flow', () => { /* ... */ });
it.skip('applies the discount', () => { /* ... */ });
test.todo('handles expired coupons');
// x-prefixed (jasmine/jest/mocha) — same effect
xit('rejects negative amounts', () => { /* ... */ });
xdescribe('payments', () => { /* ... */ });
// commented-out test — invisible to the runner AND the reporter
// it('retries on 503', async () => {
// await expect(client.fetch()).resolves.toBeOk();
// });
// JUnit: @Ignore / @Disabled with a hand-wavy reason
@Ignore("disabled for now as this test is too flaky")
@Test public void peerPriority() { /* ... */ }
Tell-tale signs in the test report: a non-zero "skipped"/"pending" count that nobody looks at, suites that have shrunk silently over time, and skips with no linked issue or expiry. Conditional returns at the top of a test (if (process.platform === 'win32') return;) are a sneakier variant that hides the skip from skip counters entirely.
##Reasons for the Problem
Why it happens
- A test started failing (a real regression, a flaky timing dependency, an environment/version change) and skipping it was the fastest way to get a green build or unblock a merge.
- A test was written ahead of the implementation (
test.todo/xitas a placeholder) and never came back. - Migrations (new framework, runtime, or API) broke the test and it was parked "temporarily."
- The skip was meant to last an afternoon; with no reminder, expiry, or tracking issue, it becomes permanent.
Why it hurts
- False confidence. A skipped test looks like coverage in the file and the PR diff, but exercises nothing. The code path it claimed to protect is now unguarded, and everyone assumes it is safe.
- Bit rot. An ignored test is never compiled against (in some languages), never refactored, and never updated. The longer it sits, the more it drifts from reality, until re-enabling it costs more than rewriting it.
- Hidden regressions. The bug or flaky behavior that prompted the skip is still there — it has just been silenced. Skipping treats the symptom (a red bar) instead of the disease.
- Noise and erosion. Standing "skipped" counts train the team to ignore the test report, which lets new skips slip in unnoticed. Dead and commented-out test code also adds reading and maintenance overhead for no benefit.
##Treatment
Treat every ignored test as a decision that must be made now, not deferred indefinitely.
- Triage each skip. For every disabled/commented/
todotest, decide: fix it, delete it, or quarantine it with an expiry and a tracked issue. "Leave it skipped forever" is not an option. - Fix and re-enable if the behavior still matters. If the test is flaky, fix the flakiness (control time, randomness, async, and shared state) rather than skipping.
- Delete it if the feature is gone or the test is obsolete. A deleted test is honest; a skipped one lies. Version control remembers it if you ever need it back — so never comment out a test instead of deleting it.
- If you must skip temporarily, make it loud and time-boxed: always include a reason and a tracking link, and prefer a mechanism that surfaces in reports and fails once the deadline passes, so the skip cannot rot.
- Stop the bleeding with lint/CI. Turn on a "no disabled tests" rule so new skips are caught in review, and treat the existing skip count as a backlog to burn down to zero.
// before — silent, permanent, untracked
it.skip('refunds the full amount on cancel', async () => {
await expect(refund(order)).resolves.toEqual({ amount: 100 });
});
// after — fixed and running again (root cause: floating-point total)
it('refunds the full amount on cancel', async () => {
await expect(refund(order)).resolves.toEqual({ amount: 100 });
});
// acceptable interim — visible, attributed, and expiring
it.skip('refunds the full amount on cancel — flaky clock, see JIRA-1234 (remove by 2026-07-01)', async () => {
/* ... */
});
// before
@Ignore("too flaky")
@Test public void peerPriority() { ... }
// after: fix the timing dependency and re-enable, or delete if obsolete
@Test public void peerPriority() { ... }
##Detected by
- eslint-jest jest/no-disabled-tests — Disallow disabled tests
- eslint-jest jest/no-commented-out-tests — Disallow commented-out tests
- eslint-vitest vitest/no-disabled-tests — Disallow disabled tests
- eslint-vitest vitest/no-commented-out-tests — Disallow commented-out tests
- sonar java:S1607 — Tests should not be ignored
- sonar javascript:S1607 — Tests should not be skipped without providing a reason