---
title: "Duplication de code de test"
type: "test-smell"
slug: "test-code-duplication"
url: "http://localhost:3000/fr/test-smells/test-code-duplication.md"
category: "Odeurs d'obscurité"
description: "La duplication de code de test survient lorsque le même code de mise en place, d’action ou d’assertion est copié-collé dans de nombreux tests, de sorte qu’un seul changement impose des modifications à de multiples endroits et que les tests pourrissent en copies fragiles et quasi identiques."
---
# Duplication de code de test

> La duplication de code de test survient lorsque le même code de mise en place, d’action ou d’assertion est copié-collé dans de nombreux tests, de sorte qu’un seul changement impose des modifications à de multiples endroits et que les tests pourrissent en copies fragiles et quasi identiques.

## Signs and Symptoms

Vous reconnaissez cette odeur lorsque les tests semblent avoir été écrits par copier-coller plutôt que par réutilisation :

* La **même construction de fixture/d’objet** est reconstruite mot pour mot en tête de test après test.
* Des **séquences d’assertions identiques** (les mêmes 3–4 appels à `expect` dans le même ordre) reviennent d’un test à l’autre.
* Les nouveaux tests sont manifestement clonés d’un ancien avec une seule ligne modifiée.
* Les **mêmes littéraux magiques** (identifiants, URL, dates, chaînes d’erreur) apparaissent encore et encore.
* Un changement d’un seul constructeur ou d’une signature d’API casse **des dizaines de tests à la fois** (chirurgie au fusil de chasse).
* Vous voyez des **tests quasi dupliqués qui ne diffèrent que par les valeurs d’entrée/attendues** — un candidat évident pour un test paramétré/par table.

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

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

```

## Reasons for the Problem

**Pourquoi cela arrive**

* Copier-coller le test précédent est la façon la plus rapide d’écrire le suivant.
* Les tests sont traités comme des « citoyens de seconde zone » — ni refactorés ni soumis au même standard DRY que le code de production.
* Il n’existe pas de fixtures partagées, de méthodes de création (Creation Methods) ou de builders de données de test, et les auteurs ne connaissent pas les tests paramétrés/pilotés par table.

**Pourquoi c’est nuisible**

* **Maintenabilité :** Meszaros relie directement cette odeur au _test fragile (Fragile Test)_ — lorsque « les mêmes séquences de code apparaissent de nombreuses fois dans de nombreux tests », un seul changement de production impose de modifier la même chose à N endroits. Le coût de maintenance croît avec le nombre de copies, et non avec le nombre de comportements distincts.
* **Lisibilité :** le code répétitif (boilerplate) ensevelit l’unique ligne qui distingue réellement chaque test, si bien que les lecteurs ne voient pas rapidement ce qui est vérifié.
* **Fiabilité :** le copier-coller invite aux erreurs de copier-coller, et les corrections sont appliquées à une copie mais pas à ses semblables, laissant des tests incohérents et contradictoires.
* **Fausse confiance :** une assertion défectueuse qui a été dupliquée est désormais fausse à de nombreux endroits à la fois, et les tests clonés dérivent silencieusement jusqu’à ne plus exercer ce que leurs noms prétendent.

**Mise en garde — DRY vs DAMP :** les tests gagnent aussi à être des _phrases descriptives et significatives (Descriptive And Meaningful Phrases)_. N’abstrayez pas à l’excès au point qu’un lecteur doive courir après des helpers pour comprendre un test. Extrayez la duplication véritable et révélatrice de l’intention ; gardez visible et local le détail essentiel propre à chaque test.

## Treatment

Éliminez la duplication _accidentelle_ tout en gardant évidente l’essence de chaque test :

1. **Extrayez des utilitaires de test / des méthodes de création (Creation Methods)** (Object Mother, Test Data Builder) pour la construction d’objets répétée, afin que chaque test ne nomme que les valeurs qui l’intéressent.
2. **Utilisez `beforeEach` / une mise en place implicite (Implicit Setup)** pour le contexte véritablement partagé et pertinent pour chaque test du bloc — mais évitez de cacher un état dont un test dépend (cela troquerait la duplication contre un _test obscur (Obscure Test)_).
3. **Extrayez des assertions personnalisées / des helpers de vérification** pour les séquences d’assertions multi-étapes récurrentes, en vérifiant idéalement une seule condition logique.
4. **Fusionnez les tests quasi identiques en tests paramétrés/pilotés par table** (`it.each` / `test.each`), afin que les paires entrée + sortie attendue vivent dans une seule table.
5. **Remplacez les littéraux magiques dupliqués** par des constantes nommées ou des valeurs par défaut de builder.

Avant — construction dupliquée et trois tests quasi identiques :

```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);
});

```

Après — une méthode de création supprime la duplication de mise en place, et une table remplace les clones :

```js
// méthode de création partagée : les tests n'indiquent que les surcharges qui importent
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);
});

```

Exécutez un détecteur de copier-coller (ci-dessous) sur vos sources de test, puis refactorez en premier les blocs les plus volumineux/les plus répétés.

## Detected by

- **eslint-sonarjs** `no-identical-functions` — Les fonctions ne devraient pas avoir des implémentations identiques (https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-identical-functions.md)
- **eslint-sonarjs** `no-duplicate-string` — Les littéraux de chaîne ne devraient pas être dupliqués (https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-duplicate-string.md)
- **sonar** `javascript:S4144` — Les fonctions ne devraient pas avoir des implémentations identiques (https://rules.sonarsource.com/javascript/RSPEC-4144/)
- **pmd** `cpd` — Copy/Paste Detector (CPD) — blocs de code dupliqués (https://pmd.github.io/pmd/pmd_userdocs_cpd.html)
