ConstructiCat Logo
CodeBust.
Browse section ▾

Empty Test.

An Empty Test is a test method with a body that contains no executable statements, so it always passes while verifying nothing.

##Signs and Symptoms

A test exists and is reported as passing, but its body has no executable code — no setup, no exercise, no assertion. Telltale forms:

// Completely empty body
test('calculates tax', () => {});

// Only a TODO / placeholder comment
it('should reject invalid input', () => {
  // TODO: write this once the API stabilises
});

// Everything is commented out
it('parses the auth header', () => {
  // const result = parse(header);
  // expect(result).toEqual(expected);
});

How to spot it:

  • The test name promises behaviour, but the body is {}, whitespace, or comments only.
  • Your runner's summary shows the test as passed (green), not skipped/pending — this is what makes it dangerous.
  • Code coverage for the named feature is suspiciously zero even though a "test for it" exists.
  • During review, the diff adds a test case but no expect/assert call.

Related but distinct: a test that has setup and calls the system under test yet makes no assertion is the Assertion-less / Unknown Test smell. An Empty Test is the more extreme case where the body is entirely devoid of statements.

##Reasons for the Problem

Why it happens

  • A test was scaffolded as a placeholder (TDD "write the name first") and never filled in.
  • Code was commented out to debug a failure or quiet a flaky test, then committed and forgotten.
  • A generator/IDE produced a skeleton test stub that nobody completed.
  • A developer wanted to "reserve" a test name for later but used an empty body instead of an explicit todo marker.

Why it hurts

  • False confidence (the worst part). Most runners report an empty test as passing, not skipped. JUnit, Jest, Vitest, and friends will all show green for test('x', () => {}). The suite looks like it covers the behaviour, but a regression in the production code will never be caught — arguably worse than having no test at all, because the green check actively misleads.
  • Readability. A reader sees a test named should reject invalid input and reasonably assumes that behaviour is verified. The name documents intent that the body never delivers.
  • Maintainability. Empty tests inflate the test count and coverage-by-name perception, hiding genuine gaps and making it hard to tell intentional placeholders from abandoned ones.
  • Erodes trust. Once contributors learn that "passing" doesn't mean "verified," the whole suite's signal weakens.

##Treatment

Decide whether the test should exist yet, then make the runner tell the truth.

1. If the behaviour should be tested now — fill in the body. Add arrange/act/assert so it actually exercises the code:

// Before — green, but verifies nothing
test('rounds half up', () => {});

// After — real expectation
test('rounds half up', () => {
  expect(round(2.5)).toBe(3);
});

2. If it is a genuine placeholder — mark it explicitly pending so the runner reports it as todo/skipped (visible, not falsely green) instead of leaving an empty body:

// Before
it('should reject invalid input', () => {
  // TODO
});

// After — shows up as a todo in the report, never as "passed"
it.todo('should reject invalid input');   // Jest / Vitest
// or test.skip(...) / xit(...) with a tracking ticket

3. If the test is obsolete — delete it. A removed test is honest; an empty one is a lie that costs maintenance.

4. Restore commented-out logic. If the body is fully commented out, either uncomment and fix it or remove the test; never ship commented-out test code as the "implementation."

5. Add a guard. Enable an assertion-presence lint rule (see detectors) in CI so empty/assertion-less tests fail the build rather than passing silently.

##Detected by

  • eslint-jest expect-expecteslint-plugin-jest: expect-expect (flags tests with no assertion, including empty bodies)
  • eslint-vitest expect-expecteslint-plugin-vitest: expect-expect (enforces at least one expectation per test)
  • eslint no-empty-functionESLint core: no-empty-function (general rule; flags the empty arrow/function body of an empty test callback)
  • sonar S2699SonarSource: S2699 "Tests should include assertions" (Blocker; flags assertion-less and empty tests; equivalents exist for Java/C#/Python)
  • pmd JUnitTestsShouldIncludeAssertPMD (Java): JUnitTestsShouldIncludeAssert (flags JUnit tests lacking an assertion, including empty tests)