ConstructiCat Logo
CodeBust.
Browse section ▾

Duplicate Assert.

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.

##Signs and Symptoms

You see the same assertion appear twice (same matcher, same arguments) in one test, or several copy‑pasted assertion blocks that differ only in literal values and re‑test the same behavior. Tell‑tale signs:

  • A literally identical assert line appears two or more times in the body.
  • Many expect(...)/assertEquals(...) calls exercising the same condition with different inputs, all crammed into one method whose name describes only a single scenario.
  • Leftover assertions from debugging ("let me also check X") that were never cleaned up.
  • The test name (e.g. testXmlSanitizer) gives no clue which of its many checks failed.
test('sanitizer accepts valid input', () => {
  expect(isValid('plain text')).toBe(true);
  expect(isValid('with spaces')).toBe(true);
  expect(isValid('Fritz-box')).toBe(true);   // "minus is valid"
  expect(isValid('Fritz-box')).toBe(true);   // <-- exact duplicate, adds nothing
  expect(isValid('<script>')).toBe(false);
});

The duplicated Fritz-box line is the canonical Duplicate Assert; the broader smell is that one method silently bundles many same-condition checks under a single name.

##Reasons for the Problem

Why it happens

  • Copy‑paste. An assert block is duplicated and the literals (or even nothing) are changed.
  • Debugging residue. Extra assertions added to probe behavior are left behind.
  • Grouping. Developers test "one method" by piling every case into a single test instead of splitting them, producing repeated, near‑identical checks.

Why it hurts

  • False confidence. A truly identical, duplicated assertion adds zero coverage — it can never fail when its twin passes — yet it makes the test look more thorough than it is.
  • Hard failure diagnosis. When several same-shaped asserts share one method, a failure report points at the method, not the specific input that broke. Worse, a default-config test stops at the first failing assertion, so later duplicated checks never run — you fix one, re-run, hit the next (overlaps with Assertion Roulette).
  • Poor readability. The reader can't tell whether the repetition is intentional (distinct cases) or a mistake (true duplicate), and the test name documents only one of many conditions.
  • Maintenance cost. Change the behavior under test and you must hunt down and update every duplicated assertion; miss one and the suite becomes inconsistent.

##Treatment

  1. Delete exact duplicates. If an assertion is byte-for-byte identical to another in the same test, remove it — it is dead weight, not coverage.
  2. Parameterize equivalent cases. When the "duplicates" are really the same condition with different inputs, convert them to a table/parameterized test (test.each in Jest/Vitest, @ParameterizedTest in JUnit 5). Each row is reported and named separately, so failures pinpoint the offending input.
  3. Split genuinely different conditions into separate tests, each with a name that states what it verifies (accepts hyphenated host names, rejects script tags).
  4. Name for intent. A test name should describe one behavior; if you can't, that's a signal to split.
// Before — duplicated / bundled asserts in one opaque test
test('isValid', () => {
  expect(isValid('plain text')).toBe(true);
  expect(isValid('with spaces')).toBe(true);
  expect(isValid('Fritz-box')).toBe(true);
  expect(isValid('Fritz-box')).toBe(true); // duplicate
  expect(isValid('<script>')).toBe(false);
});

// After — one assertion, each case named and reported independently
test.each([
  ['plain text', true],
  ['with spaces', true],
  ['Fritz-box',  true],   // minus is valid
  ['<script>',   false],  // rejects markup
])('isValid(%j) === %s', (input, expected) => {
  expect(isValid(input)).toBe(expected);
});

The duplicate row is gone, distinct inputs are explicit, and a failure names the exact case.