---
title: "Duplicate Assert"
type: "test-smell"
slug: "duplicate-assert"
url: "http://localhost:3000/en/test-smells/duplicate-assert.md"
category: "Assertion Smells"
description: "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."
---
# 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.

```js
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.

```js
// 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.
