Test Smells.
Recurring problems in test code — how to recognize each one, why it hurts, how to fix it, and the lint rules that detect it.
A test packs many undocumented assertions into one method, so when it fails you can't tell which assertion fired or why — you have to gamble.
An Eager Test verifies several distinct methods or behaviors of the unit under test in a single test method, instead of focusing on one behavior.
A redundant assertion compares a value against itself or against a literal that is equal by construction, so its outcome is fixed and it can never actually fail or detect a regression.
A test method that exercises the code but contains no assertion, so it passes as long as nothing throws — leaving its actual purpose and what it verifies unknown.
A single test method verifies the same condition more than once — repeating an identical assertion or re-checking equivalent logic — instead of removing the redundant check or splitting distinct cases into their own focused tests.
A test verifies behavior by comparing an object's string representation (toString(), JSON.stringify(), rendered HTML) against an expected string literal, coupling the test to incidental formatting rather than the values that actually matter.
A shared setup builds one big "kitchen-sink" fixture covering every test's needs, but each individual test exercises only a small slice of it.
A test whose inputs or expected results live in an external resource — a file, database seed, or shared fixture — so you can't understand or trust the test by reading it alone.
Resource Optimism is when a test assumes an external resource (a file, directory, database table, env var, or network endpoint) already exists and is in a known state instead of provisioning and verifying it, making the test pass or fail non-deterministically.
A test class initializes its fixture fields in a constructor instead of the framework's dedicated setup hook (setUp / @BeforeEach / TestInitialize), bypassing the test lifecycle.
An Obscure Test is one where a reader cannot tell, from the test method alone, what scenario is set up, what behavior is exercised, and what outcome is expected — because the intent is buried in too much detail, too little context, or logic hidden elsewhere.
A test that uses `if`/`switch`/ternaries, loops, or `try`/`catch` to decide what to run or assert, so its behavior — and whether it verifies anything at all — depends on which branch executes at runtime.
Test Code Duplication is when the same setup, action, or assertion code is copy-pasted across many tests, so a single change forces edits in many places and tests rot into fragile, near-identical copies.
A test hard-codes unexplained numeric literals in its inputs and assertions, hiding what the numbers mean and where they came from.
A test that uses far more code than it needs to state its scenario, burying the one cause-and-effect it verifies under setup boilerplate, irrelevant data, and field-by-field assertions.
An erratic (flaky) test passes and fails intermittently on the same code because its outcome depends on timing, ordering, shared state, or other nondeterministic factors rather than the behavior under test.
A Sleepy Test pauses execution with a hard-coded delay (`Thread.sleep`, `setTimeout`, `cy.wait(2000)`, `page.waitForTimeout`) to wait for asynchronous work instead of waiting on the actual condition.
A Test Run War is when tests pass for one person but fail randomly the moment several people or CI jobs run the suite at the same time, because the tests collide on a shared, persistent fixture.
A test passes or fails depending on which other tests ran before it, because tests leak and rely on shared mutable state instead of each setting up and tearing down its own fixture.
A test wires up so many mock objects and stubbed interactions that the mock setup dwarfs the actual verification, so the test ends up exercising the mocks rather than real behavior.
A test asserts on how the code works internally — private fields, internal method calls, DOM structure, or CSS classes — instead of the observable behavior a real consumer depends on.
An overspecified test asserts far more than the behavior under test requires—pinning exact output strings, full object shapes, collection order, or every internal collaborator call—so it breaks whenever an unrelated implementation detail changes.
An Empty Test is a test method with a body that contains no executable statements, so it always passes while verifying nothing.
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.
Production code contains logic, branches, or members that exist only to support testing, blurring the line between what ships and what is merely tested.
Production code contains methods, state accessors, or seams that exist only to be used by tests, polluting the real API and inviting tests that verify internals instead of behavior.
Production code whose design (tight coupling, hidden dependencies, global state, non-deterministic IO, or async-only interfaces) forces tests into awkward contortions, or makes a unit impossible to exercise in isolation at all.