---
title: "Test sur-spécifié"
type: "test-smell"
slug: "overspecified-test"
url: "http://localhost:3000/fr/test-smells/overspecified-test.md"
category: "Odeurs de mock"
description: "Un test sur-spécifié vérifie bien plus que ce qu'exige le comportement testé — figeant des chaînes de sortie exactes, des formes d'objet complètes, l'ordre d'une collection ou chaque appel de collaborateur interne — de sorte qu'il casse dès qu'un détail d'implémentation sans rapport change."
---
# Test sur-spécifié

> Un test sur-spécifié vérifie bien plus que ce qu'exige le comportement testé — figeant des chaînes de sortie exactes, des formes d'objet complètes, l'ordre d'une collection ou chaque appel de collaborateur interne — de sorte qu'il casse dès qu'un détail d'implémentation sans rapport change.

## Signs and Symptoms

Vous reconnaissez un test sur-spécifié à ce qui le casse : une refactorisation anodine ou un changement accessoire fait échouer des tests alors même que le comportement qu'ils décrivent reste correct.

Signaux courants :

* **Assertions sur des détails non pertinents.** Espaces/balisage exacts, chaînes de messages d'erreur complètes, texte de log, formatage, ou valeurs volatiles comme les horodatages, les UUID et les identifiants auto-incrémentés.
* **Égalité d'objet entier alors qu'un seul champ compte.** `toEqual({...l'objet entier...})` alors que le test porte en réalité sur une seule propriété.
* **Assertions sensibles à l'ordre sur des données logiquement non ordonnées.** Figer l'ordre d'un ensemble, des clés d'une map, ou des résultats d'une requête que le contrat ne garantit pas.
* **Vérification du comportement de collaborateurs internes.** Des mocks qui assertent des comptages d'appels exacts, des valeurs argument par argument et l'ordre d'appel de dépendances _internes_ au lieu du résultat final observable.
* **Snapshots géants** qui capturent un arbre rendu entier ou un corps de réponse complet, de sorte que tout changement imbriqué force un réenregistrement.
* **Plonger dans l'implémentation.** Interroger l'état privé ou la structure du DOM (`container.querySelector`, champs internes) plutôt que le comportement public, observable par l'utilisateur.

```js
// Sur-spécifié : fige le balisage exact, un horodatage volatil et chaque appel de collaborateur
test('renders user badge', () => {
  const html = renderBadge(user);
  expect(html).toBe('<span class="badge badge--admin">Ada Lovelace</span>'); // balisage exact
  expect(analytics.track).toHaveBeenCalledTimes(1);                          // nombre d'appels internes
  expect(analytics.track).toHaveBeenCalledWith('badge_render', { ts: 1718445693221 }); // volatil
});

```

Un test décisif utile : si vous pouvez changer l'implémentation sans changer le comportement documenté et que le test échoue quand même, il est sur-spécifié.

## Reasons for the Problem

**Pourquoi cela se produit**

* Un état d'esprit « plus d'assertions = plus de rigueur » : les développeurs figent tout ce qu'ils voient, confondant _complet_ et _correct_.
* L'outillage fait de la sur-spécification la voie de moindre résistance : `toMatchSnapshot()` enregistre la sortie entière, et les frameworks de mock rendent trivial le fait d'asserter chaque interaction (`toHaveBeenCalledWith`, ordre des appels, comptages).
* Copier-coller un littéral d'objet réel dans `toEqual` au lieu de n'asserter que le champ qui concerne le test.
* Tester via des points d'accès d'implémentation pratiques (nœuds du DOM, champs privés) plutôt que via le contrat public.

**Pourquoi c'est nuisible**

Dans _xUnit Test Patterns_ de Gerard Meszaros, c'est la cause profonde du smell **Test fragile**, attribuée au **logiciel sur-spécifié** et à la **sensibilité au comportement** : les tests sont couplés à la _manière_ dont le code fonctionne plutôt qu'à _ce_ qu'il produit, si bien qu'ils échouent sur des changements qui n'affectent pas le comportement.

* **Maintenabilité.** Chaque refactorisation déclenche une cascade d'échecs de tests sans rapport, rendant la suite coûteuse à garder au vert et décourageant activement la refactorisation.
* **Fiabilité / confiance.** Les tests qui « crient au loup » sur du code correct entraînent l'équipe à ignorer les échecs ou à les réenregistrer aveuglément (surtout avec les snapshots).
* **Fausse confiance.** La sur-spécification s'accompagne souvent d'une sous-assertion du résultat _significatif_ : un test peut figer un horodatage et une ligne de log sans jamais vérifier la valeur qui importe réellement à l'utilisateur. Comme le dit la littérature sur les tests d'interaction, la sur-spécification consiste à « vérifier des choses qui ne font pas partie du résultat final » — le plus souvent en assertant des interactions plutôt que des résultats.
* **Lisibilité.** Un mur d'assertions accessoires masque l'unique comportement que le test est censé documenter.

## Treatment

N'assertez que ce qu'exige le comportement testé — et préférez vérifier le **résultat final** plutôt que les interactions internes.

1. **Nommez l'unique résultat que le test documente** et assertez-le, rien de plus. Scindez les résultats véritablement distincts en tests séparés et bien nommés, plutôt qu'en une seule méga-assertion.
2. **Utilisez des matchers partiels/souples** au lieu de l'égalité exacte d'objet entier : `expect.objectContaining`, `toMatchObject`, `arrayContaining`, `stringContaining`/`stringMatching`, et `expect.any(...)` pour les champs que vous ne pouvez pas ou ne devriez pas figer.
3. **Neutralisez les données volatiles** (horodatages, identifiants, valeurs aléatoires) avec des matchers de propriété (`expect.any(String)`) ou en injectant une horloge/un générateur d'identifiants fixe — ne codez pas en dur la valeur du jour.
4. **Abandonnez les hypothèses d'ordre** pour les données non ordonnées : assertez l'appartenance (`arrayContaining`) ou triez avant de comparer.
5. **Préférez la vérification d'état à la vérification de comportement.** Stubbez les _requêtes_ (ne les assertez pas) ; ne vérifiez l'appel d'un collaborateur que lorsque cet appel _est_ l'effet de bord observable (une commande), et évitez d'asserter les comptages/l'ordre exacts de collaborateurs purement internes.
6. **Gardez les snapshots petits et intentionnels**, ou remplacez un snapshot tentaculaire par quelques assertions ciblées. Interrogez via l'API destinée à l'utilisateur (par ex. `getByRole`/`getByText` de Testing Library) plutôt que par accès à `container`/aux nœuds du DOM.

Avant → après :

```js
// Avant : couple le test à des champs volatils et à l'ordonnancement des appels internes
expect(result).toEqual({
  id: '8f3c-92a1-...',                  // uuid aléatoire
  createdAt: '2026-06-15T10:01:33.221Z',// Date.now()
  name: 'Ada Lovelace',
  role: 'admin',
});
expect(logger.info).toHaveBeenCalledTimes(3);
expect(db.connect).toHaveBeenCalledBefore(db.query);

// Après : n'asserter que le comportement visé par ce test
expect(result).toMatchObject({ name: 'Ada Lovelace', role: 'admin' });
// id/createdAt sont accessoires ; la journalisation et l'ordre des appels sont des détails d'implémentation — ne les figez pas

```

## Detected by

- **eslint-jest** `jest/no-large-snapshots` — no-large-snapshots (https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/no-large-snapshots.md)
- **eslint-vitest** `vitest/no-large-snapshots` — no-large-snapshots (https://github.com/vitest-dev/eslint-plugin-vitest/blob/main/docs/rules/no-large-snapshots.md)
- **eslint-testing-library** `testing-library/no-node-access` — no-node-access (https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/no-node-access.md)
- **eslint-testing-library** `testing-library/no-container` — no-container (https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/no-container.md)
