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/assertcall.
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 inputand 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-expect — eslint-plugin-jest: expect-expect (flags tests with no assertion, including empty bodies)
- eslint-vitest expect-expect — eslint-plugin-vitest: expect-expect (enforces at least one expectation per test)
- eslint no-empty-function — ESLint core: no-empty-function (general rule; flags the empty arrow/function body of an empty test callback)
- sonar S2699 — SonarSource: S2699 "Tests should include assertions" (Blocker; flags assertion-less and empty tests; equivalents exist for Java/C#/Python)
- pmd JUnitTestsShouldIncludeAssert — PMD (Java): JUnitTestsShouldIncludeAssert (flags JUnit tests lacking an assertion, including empty tests)