---
title: "Duplicación de código en los tests"
type: "test-smell"
slug: "test-code-duplication"
url: "http://localhost:3000/es/test-smells/test-code-duplication.md"
category: "Olores oscuros"
description: "La duplicación de código en los tests se da cuando el mismo código de configuración, acción o aserción se copia y pega en muchos tests, de modo que un solo cambio obliga a editar en muchos lugares y los tests se pudren convirtiéndose en copias frágiles casi idénticas."
---
# Duplicación de código en los tests

> La duplicación de código en los tests se da cuando el mismo código de configuración, acción o aserción se copia y pega en muchos tests, de modo que un solo cambio obliga a editar en muchos lugares y los tests se pudren convirtiéndose en copias frágiles casi idénticas.

## Signs and Symptoms

Reconoces este mal olor cuando los tests parecen escritos a copia y pega en lugar de mediante reutilización:

* La **misma construcción de fixture/objeto** se reconstruye literalmente al principio de un test tras otro.
* Se repiten en varios tests **secuencias de aserciones idénticas** (las mismas 3-4 llamadas a `expect` en el mismo orden).
* Los nuevos tests son, evidentemente, clones de uno antiguo con una sola línea retocada.
* Los **mismos literales mágicos** (IDs, URLs, fechas, cadenas de error) aparecen una y otra vez.
* Un cambio en un constructor o en la firma de una API rompe **docenas de tests a la vez** (cirugía de escopeta).
* Ves **tests casi duplicados que solo difieren en los valores de entrada/esperados**: un claro candidato para un test parametrizado/de tabla.

```js
test('flight can be cancelled', () => {
  const airport = new Airport('YYC', 'Calgary');                              // duplicado
  const flight  = new Flight('AC123', airport, new Date('2026-06-01T10:00')); // duplicado
  flight.cancel();
  expect(flight.status).toBe('CANCELLED');
});

test('flight can be delayed', () => {
  const airport = new Airport('YYC', 'Calgary');                              // copia y pega
  const flight  = new Flight('AC123', airport, new Date('2026-06-01T10:00')); // copia y pega
  flight.delay(30);
  expect(flight.status).toBe('DELAYED');
});

```

## Reasons for the Problem

**Por qué ocurre**

* Copiar y pegar el test anterior es la forma más rápida de escribir el siguiente.
* Los tests se tratan como «ciudadanos de segunda clase»: no se refactorizan ni se les exige el mismo estándar DRY que al código de producción.
* No existen fixtures compartidos, Creation Methods ni constructores de datos de test, y los autores no están familiarizados con los tests parametrizados o basados en tablas.

**Por qué es perjudicial**

* **Mantenibilidad:** Meszaros vincula este mal olor directamente con el _Fragile Test_: cuando «las mismas secuencias de código aparecen muchas veces en muchos tests», un único cambio en producción implica editar lo mismo en N lugares. El coste de mantenimiento escala con el número de copias, no con el número de comportamientos distintos.
* **Legibilidad:** el boilerplate repetido entierra la única línea que realmente hace distinto a cada test, así que los lectores no pueden ver rápidamente qué se está verificando.
* **Fiabilidad:** copiar y pegar invita a errores de copiar y pegar, y las correcciones se aplican a una copia pero no a sus hermanas, dejando tests inconsistentes y contradictorios.
* **Falsa confianza:** una aserción defectuosa que se duplicó ahora es incorrecta en muchos lugares a la vez, y los tests clonados van a la deriva en silencio hasta que dejan de ejercitar lo que sus nombres afirman.

**Advertencia: DRY frente a DAMP:** los tests también valoran ser _Descriptive And Meaningful Phrases_ (frases descriptivas y significativas). No abstraigas en exceso hasta el punto de que un lector tenga que perseguir helpers para entender un test. Extrae la duplicación genuina que revela la intención; mantén el detalle esencial de cada test visible y local.

## Treatment

Elimina la duplicación _incidental_ manteniendo evidente la esencia de cada test:

1. **Extrae utilidades de test / Creation Methods** (Object Mother, Test Data Builder) para la construcción de objetos repetida, de modo que cada test nombre solo los valores que le importan.
2. **Usa `beforeEach` / Implicit Setup** para el contexto que es genuinamente compartido y relevante para todos los tests del bloque, pero evita ocultar el estado del que depende un test (eso cambia la duplicación por un _Obscure Test_).
3. **Extrae Custom Assertions / helpers de verificación** para las secuencias de aserciones de varios pasos que se repiten, idealmente verificando una única condición lógica.
4. **Colapsa los tests casi idénticos en tests parametrizados / de tabla** (`it.each` / `test.each`) para que los pares de entrada + salida esperada vivan en una sola tabla.
5. **Reemplaza los literales mágicos duplicados** por constantes con nombre o valores por defecto del builder.

Antes: construcción duplicada y tres tests casi idénticos:

```js
test('rejects negative amount', () => {
  expect(() => validateAmount(-1)).toThrow(RangeError);
});
test('rejects zero amount', () => {
  expect(() => validateAmount(0)).toThrow(RangeError);
});
test('rejects NaN amount', () => {
  expect(() => validateAmount(NaN)).toThrow(RangeError);
});

```

Después: un Creation Method elimina la duplicación de configuración y una tabla reemplaza los clones:

```js
// Creation Method compartido: los tests indican solo los overrides que importan
const aFlight = (overrides = {}) =>
  new Flight('AC123', new Airport('YYC', 'Calgary'),
             new Date('2026-06-01T10:00'), overrides);

it.each([-1, 0, NaN])('rejects invalid amount %p', (amount) => {
  expect(() => validateAmount(amount)).toThrow(RangeError);
});

```

Ejecuta un detector de copia y pega (más abajo) sobre tus fuentes de test y, a continuación, refactoriza primero los bloques más grandes/más repetidos.

## Detected by

- **eslint-sonarjs** `no-identical-functions` — Las funciones no deben tener implementaciones idénticas (https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-identical-functions.md)
- **eslint-sonarjs** `no-duplicate-string` — Los literales de cadena no deben duplicarse (https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-duplicate-string.md)
- **sonar** `javascript:S4144` — Las funciones no deben tener implementaciones idénticas (https://rules.sonarsource.com/javascript/RSPEC-4144/)
- **pmd** `cpd` — Copy/Paste Detector (CPD) — bloques de código duplicados (https://pmd.github.io/pmd/pmd_userdocs_cpd.html)
