---
title: "Número mágico en tests"
type: "test-smell"
slug: "magic-number-test"
url: "http://localhost:3000/es/test-smells/magic-number-test.md"
category: "Olores oscuros"
description: "Un test codifica de forma rígida literales numéricos sin explicar en sus entradas y aserciones, ocultando qué significan los números y de dónde salieron."
---
# Número mágico en tests

> Un test codifica de forma rígida literales numéricos sin explicar en sus entradas y aserciones, ocultando qué significan los números y de dónde salieron.

## Signs and Symptoms

Detectas un Magic Number Test cuando los argumentos y las aserciones de un test están llenos de números sueltos cuyo significado y origen no son evidentes a partir del código. El lector tiene que aplicar ingeniería inversa (o simplemente confiar) para entender por qué se espera un valor concreto.

Señales reveladoras:

* Aparecen literales numéricos directamente como argumentos de aserción: `expect(result).toBe(54.13)`, `assertEquals(86400, ttl)`.
* El mismo literal se repite en la configuración, la acción y el valor esperado, sin ningún nombre que los relacione.
* Los números codifican conceptos de dominio que no se explicitan (`3600` \= una hora, `200` \= HTTP OK, `0.0825` \= un tipo impositivo).
* Un comentario de código se coloca junto al número para explicarlo, señal de que el número en sí debería haber tenido un nombre.
* Durante la revisión la gente pregunta «¿por qué `42`?» o «¿de dónde sale `54.13`?» y nadie puede responder sin volver a ejecutar el código.

```js
// Mal olor: ¿qué es 8.25? ¿por qué 54.13? ¿qué es ese 50 oculto dentro del helper?
test('checkout works', () => {
  const total = checkout(cartFor(50), 8.25);
  expect(total).toBe(54.13);
});

```

Esto es una variante específica, del lado de los tests, del mal olor general _Magic Number_, y un contribuyente clásico al mal olor **Obscure Test** (Meszaros): el lector no puede entender el test solo a partir del test.

## Reasons for the Problem

**Por qué ocurre**

* El literal es el camino de menor resistencia: escribes el valor que viste en un depurador o copias la salida real de una ejecución fallida en la aserción hasta que pasa a verde («adivina el valor» / pegado de salidas).
* El autor ya tiene en la cabeza el contexto del dominio, así que `3600` u `8.25` le parecen evidentes _en el momento de escribirlo_.
* Los valores del fixture se eligen de forma arbitraria (`new User(25, ...)`) solo para satisfacer al constructor, sin pensar en su significado.

**Por qué es perjudicial**

* **Legibilidad / intención.** Un número como `54.13` expresa un hecho, pero no una razón. Los revisores y los futuros responsables del mantenimiento no pueden saber si es una expectativa deliberada, un límite o un accidente. El test deja de ser documentación ejecutable.
* **Mantenibilidad.** Cuando la regla cambia (el tipo impositivo, el timeout, el tamaño de página), tienes que rastrear cada copia del literal y saber qué `7` significaba «días» frente a «reintentos máximos». La duplicación sin nombre hace que las modificaciones seguras sean caras y propensas a errores.
* **Fiabilidad / falsa confianza.** Si el valor esperado es incorrecto —o correcto solo por casualidad—, nada en el test lo revela. Peor aún, a menudo se «arregla» un test con números mágicos recalculando el valor esperado con la fórmula de _producción_ (`expect(total).toBe(subtotal * (1 + rate)))`), convirtiendo la aserción en una tautología que reimplementa el código bajo prueba y nunca puede fallar por la razón correcta.
* **Diagnóstico.** Cuando un test así se rompe, el mensaje de fallo es simplemente «expected 54.13, got 54.12», sin pista alguna sobre qué entrada o regla produjo el número, lo que ralentiza la depuración.

## Treatment

Aplica **Replace Magic Number with Symbolic Constant** (Meszaros): dale a cada valor significativo un nombre que explique su papel, y haz explícita la relación entre las entradas y el resultado esperado.

Pasos concretos:

1. **Nombra las entradas.** Extrae los literales usados como datos de test a constantes locales bien nombradas o a llamadas a un constructor de fixtures (`const SUBTOTAL = 50.00`, `const TAX_RATE_PCT = 8.25`). Usa un Object Mother / builder para los fixtures de objetos, de modo que solo sean visibles los valores que importan al test.
2. **Nombra y explica el valor esperado.** Mantén el resultado esperado como un literal _independiente_, pero nómbralo y documenta cómo se obtuvo (`const EXPECTED_TOTAL = 54.13; // 50.00 + 8.25% de impuesto`). **No** lo recalcules con la fórmula de producción: eso solo vuelve a probar el código contra sí mismo.
3. **Relaciona las entradas con la aserción** para que un lector pueda verificar la aritmética a simple vista, o haz la aserción contra un valor de referencia derivado pero independiente.
4. **Deja en paz los valores realmente evidentes por sí mismos.** `0`, `1`, `-1`, los índices de array y los conteos obvios (`items).toHaveLength(2)`) normalmente no necesitan nombres; reserva las constantes para los valores cuyo significado no se explica por sí solo. Promueve una constante compartida a una única fuente de verdad solo cuando se trate verdaderamente del mismo concepto en todas partes.

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

// Después
const SUBTOTAL = 50.00;
const TAX_RATE_PCT = 8.25;
const EXPECTED_TOTAL = 54.13; // SUBTOTAL más un 8.25% de impuesto sobre ventas

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

```

Ahora los nombres llevan la intención; si la regla del impuesto cambia, la modificación es local y evidente, y el valor esperado sigue siendo una comprobación honesta e independiente en lugar de una tautología.

## Detected by

- **eslint** `no-magic-numbers` — Los números mágicos deben declararse como constantes con nombre (https://eslint.org/docs/latest/rules/no-magic-numbers)
- **typescript-eslint** `@typescript-eslint/no-magic-numbers` — Prohibir números mágicos (TypeScript) (https://typescript-eslint.io/rules/no-magic-numbers/)
- **sonar** `javascript:S109` — No deben usarse números mágicos (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 (detector específico de tests) (https://testsmells.org/pages/testsmells.html)
