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

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

## Signs and Symptoms

An assertion is **redundant** when its result is decided before the code under test even runs — the expected and actual operands are the _same value_, or both are literals known to be equal (or unequal). The xUnit/Test Smells catalog (Peruma et al.) defines it as "a test method that contains an assertion statement in which the expected and actual parameters are the same," and notes the assertion is therefore "either always true or always false."

How to spot it:

* An `assertEquals`/`toBe`/`toEqual` whose two arguments are the **same expression** or variable.
* A boolean literal asserted against itself, e.g. `assertTrue(true)` or `expect(true).toBe(true)`.
* A comparison that the compiler/linter can prove is constant, like `expect(x === x).toBe(true)`.
* A "sanity check" that restates a constant you just declared instead of exercising the system.

```js
// Smell: outcome is fixed, code under test is never involved
test('user is active', () => {
  expect(true).toBe(true);            // always passes
  const status = 'active';
  expect(status).toBe('active');      // restates the literal, proves nothing
  expect(user.id).toEqual(user.id);   // value compared to itself
});

```

A reliable tell: you can delete the production code entirely and the assertion still passes green.

## Reasons for the Problem

**Why it happens**

* **Leftover debugging.** The catalog explicitly notes this smell "is introduced by developers for debugging purposes and then forgotten" — a hard‑coded `assertTrue(true)` placeholder that survives into the commit.
* **Copy‑paste / refactor drift.** A variable is substituted into both sides of an `assertEquals`, or a value-under-test is renamed so expected and actual collapse to the same symbol.
* **Tautology by construction.** Asserting a value against the literal it was just assigned from, instead of against an independently‑derived expectation.
* **Coverage theater.** An assertion is added only to satisfy "every test must assert" rules, without checking anything meaningful.

**Why it hurts**

* **False confidence.** The test is permanently green and counts toward the suite size and coverage, yet verifies nothing. It cannot catch a regression, so it masks gaps in the safety net.
* **Reliability is meaningless.** A test that can never fail provides zero signal; one that is always false is dead weight that gets ignored or `skip`ped.
* **Readability.** Readers waste effort reconstructing the intended behavior from an assertion that asserts a tautology; the test no longer documents a requirement.
* **Maintainability.** Redundant assertions accumulate noise, inflate metrics, and erode trust in the suite, encouraging people to stop reading assertions carefully.

## Treatment

Replace the tautology with a check that ties an **independently known expected value** to the **actual result produced by the system under test**.

Steps:

1. **Identify the fixed-outcome assertion** — same operand on both sides, or two equal literals.
2. **Determine the real intent.** What behavior was this test meant to verify? If nothing, the assertion (or the whole test) is dead and should be removed.
3. **Assert the SUT's output, not the input.** Feed the system real input and compare its computed result to a hard‑coded, hand‑calculated expected value — not to one of its own inputs/variables.
4. **Keep expected/actual distinct.** Ensure the expected operand is a constant you wrote on purpose and the actual operand is the return value of the code under test (also fixes the related wrong-argument-order smell).
5. **Re-run with the implementation broken** (mutate it) to confirm the assertion can actually fail.

```js
// Before — redundant: outcome is fixed
test('discount', () => {
  const total = 100;
  expect(total).toBe(100);          // restates the literal
  expect(applyDiscount).toBe(applyDiscount); // value vs itself
});

// After — meaningful: known input -> independently expected output
test('applies a 10% discount', () => {
  expect(applyDiscount(100, 0.1)).toBe(90); // SUT result vs hand-computed expectation
});

```

If a placeholder like `assertTrue(true)` was left from debugging, delete it; if it stood in for a real check, write that check.

## Detected by

- **sonar** `javascript:S5863` — Assertions should not be given twice the same argument (https://rules.sonarsource.com/javascript/RSPEC-5863/)
- **sonar** `java:S5863` — Assertions should not be given twice the same argument (https://rules.sonarsource.com/java/RSPEC-5863/)
- **eslint** `no-constant-binary-expression` — Disallow expressions where the operation does not affect the value (flags self-comparisons / always-true assertions like assert(a === a)) (https://eslint.org/docs/latest/rules/no-constant-binary-expression)
