An antipattern is an approach that seems sensible at first but creates trouble in practice. In automated testing, antipatterns often make tests less trustworthy, harder to read, and more expensive to maintain. The problem is not merely that a test looks untidy: test logic can contain bugs, test names can become vague, and harmless production changes can cause failures that reveal nothing useful. section 7.3.2 section 8.3.1
A useful distinction is between testing an observable outcome and testing incidental implementation details. A test should check an exit point: a returned value for a value-based test, or an affected sibling property or function for a state-based test. It should not normally care which protected helper was called internally. Otherwise, a refactoring can break the test even when the behavior users rely on is unchanged. section 8.3.1
Avoid a monster test
A test that loops through several name inputs and uses an if/else to decide each expected result combines multiple scenarios and repeats production logic. Split it into focused tests with hardcoded inputs and assertions instead. Each test can then name one behavior clearly, be recreated easily when it fails, and avoid bugs in its own control flow. section 7.3.2
Assert what matters, not every detail
A full-array equality assertion can accidentally require a particular result order and object schema. If the behavior being tested is whether each input passes, assert those result values directly; when ordering is irrelevant, locate the result for a given input rather than indexing the array. Similarly, match the meaningful part of a generated message instead of requiring its entire wording when only that part matters. section 8.3.2
Common testing antipatterns and their consequences include:
- Putting loops, branches, or duplicated production logic in one broad test. The test becomes harder to understand, reproduce, name, and trust. section 7.3.2
- Mocking and verifying a protected internal helper instead of the value or state exposed by the unit of work. The test becomes coupled to implementation details. section 8.3.1
- Asserting an entire collection, exact ordering, or complete strings when those details are not the behavior under test. Small irrelevant changes then create maintenance work. section 8.3.2
- Relying almost entirely on unit or component tests. Fast feedback remains useful, but it may not provide enough confidence that the application works as an integrated whole. section 10.2.2
The remedy is selective, not absolute. Keep unit tests, but favor small focused scenarios and value-based checks where possible. Retain complex or monster tests only as additional tests rather than replacements for simpler ones, and keep their number limited. At the strategy level, combine low-level tests with higher-level tests so fast feedback is balanced with confidence in integrated behavior. section 7.3.2 section 8.3.1 section 10.2.2
Listing 8.14 Overspecifying order and schema of the result
test("overspecify order and schema", () => {
const pv5 =
new PasswordVerifier5([input => input.includes("abc")]);
const results = pv5.verify(["a", "ab", "abc", "abcd"]);
expect(results).toEqual([ #1
{ input: "a", result: false }, #1
{ input: "ab", result: false }, #1
{ input: "abc", result: true }, #1
{ input: "abcd", result: true }, #1
]);
});
Compare the full collection assertion with narrower alternatives to see how an assertion can accidentally lock down order and object structure. section 8.3.2




