---
title: "Magic Number Test"
type: "test-smell"
slug: "magic-number-test"
url: "http://localhost:3000/en/test-smells/magic-number-test.md"
category: "Obscure Smells"
description: "A test hard-codes unexplained numeric literals in its inputs and assertions, hiding what the numbers mean and where they came from."
---
# Magic Number Test

> A test hard-codes unexplained numeric literals in its inputs and assertions, hiding what the numbers mean and where they came from.

## Signs and Symptoms

You spot a Magic Number Test when a test's arguments and assertions are full of bare numbers whose meaning and origin are not obvious from the code. The reader has to reverse-engineer (or just trust) why a particular value is expected.

Tell-tale signs:

* Numeric literals appear directly as assertion arguments: `expect(result).toBe(54.13)`, `assertEquals(86400, ttl)`.
* The same literal is repeated across setup, action, and expected value, with no name tying them together.
* Numbers encode domain concepts that aren't spelled out (`3600` \= an hour, `200` \= HTTP OK, `0.0825` \= a tax rate).
* A code comment sits next to the number to explain it — a sign the number itself should have been named.
* During review people ask "why `42`?" or "where does `54.13` come from?" and nobody can answer without re-running the code.

```js
// Smell: what is 8.25? why 54.13? what's 50 hidden inside the helper?
test('checkout works', () => {
  const total = checkout(cartFor(50), 8.25);
  expect(total).toBe(54.13);
});

```

This is a specific, test-side flavor of the general _Magic Number_ smell, and a classic contributor to the **Obscure Test** smell (Meszaros): the reader can't understand the test from the test alone.

## Reasons for the Problem

**Why it happens**

* The literal is the path of least resistance: you type the value you saw in a debugger or copy the actual output from a failing run into the assertion until it goes green ("Guess the value" / output-pasting).
* The author already has the domain context in their head, so `3600` or `8.25` feels self-evident _at the time of writing_.
* Fixture values get picked arbitrarily (`new User(25, ...)`) just to make the constructor happy, with no thought to meaning.

**Why it hurts**

* **Readability / intent.** A number like `54.13` states a fact but not a reason. Reviewers and future maintainers can't tell whether it's a deliberate expectation, a boundary, or an accident. The test stops being executable documentation.
* **Maintainability.** When the rule changes (the tax rate, the timeout, the page size), you must hunt down every copy of the literal and know which `7` meant "days" versus "max retries." Unnamed duplication makes safe edits expensive and error-prone.
* **Reliability / false confidence.** If the expected value is wrong — or right only by coincidence — nothing in the test reveals it. Worse, authors often "fix" a magic-number test by recomputing the expected value with the _production_ formula (`expect(total).toBe(subtotal * (1 + rate)))`), turning the assertion into a tautology that re-implements the code under test and can never fail for the right reason.
* **Diagnosis.** When such a test breaks, the failure message is just "expected 54.13, got 54.12" with no clue about which input or rule produced the number, slowing debugging.

## Treatment

Apply **Replace Magic Number with Symbolic Constant** (Meszaros): give every meaningful value a name that explains its role, and make the relationship between inputs and the expected result explicit.

Concrete steps:

1. **Name the inputs.** Extract literals used as test data into well-named local constants or fixture-builder calls (`const SUBTOTAL = 50.00`, `const TAX_RATE_PCT = 8.25`). Use an Object Mother / builder for object fixtures so only the values that matter to the test are visible.
2. **Name and explain the expected value.** Keep the expected result as an _independent_ literal, but name it and document how it was derived (`const EXPECTED_TOTAL = 54.13; // 50.00 + 8.25% tax`). Do **not** recompute it with the production formula — that just retests the code against itself.
3. **Tie inputs to the assertion** so a reader can verify the arithmetic by eye, or assert against a derived-but-independent reference value.
4. **Leave genuinely self-evident values alone.** `0`, `1`, `-1`, array indexes, and obvious counts (`items).toHaveLength(2)`) usually don't need names; reserve constants for values whose meaning isn't self-explanatory. Promote a shared constant to a single source of truth only when it's truly the same concept everywhere.

```js
// Before
test('checkout works', () => {
  const total = checkout(cartFor(50), 8.25);
  expect(total).toBe(54.13);
});

// After
const SUBTOTAL = 50.00;
const TAX_RATE_PCT = 8.25;
const EXPECTED_TOTAL = 54.13; // SUBTOTAL plus 8.25% sales tax

test('applies sales tax to the subtotal', () => {
  const total = checkout(cartFor(SUBTOTAL), TAX_RATE_PCT);
  expect(total).toBe(EXPECTED_TOTAL);
});

```

The names now carry the intent; if the tax rule changes, the edit is local and obvious, and the expected value stays an honest, independent check rather than a tautology.

## Detected by

- **eslint** `no-magic-numbers` — Magic numbers should be declared as named constants (https://eslint.org/docs/latest/rules/no-magic-numbers)
- **typescript-eslint** `@typescript-eslint/no-magic-numbers` — Disallow magic numbers (TypeScript) (https://typescript-eslint.io/rules/no-magic-numbers/)
- **sonar** `javascript:S109` — Magic numbers should not be used (https://rules.sonarsource.com/javascript/RSPEC-109/)
- **checkstyle** `MagicNumber` — MagicNumber (https://checkstyle.sourceforge.io/checks/coding/magicnumber.html)
- **tsDetect** `Magic Number Test` — Magic Number Test (test-specific detector) (https://testsmells.org/pages/testsmells.html)
