---
title: "Fixture générale"
type: "test-smell"
slug: "general-fixture"
url: "http://localhost:3000/fr/test-smells/general-fixture.md"
category: "Odeurs de fixture"
description: "Une configuration partagée construit une grande fixture « fourre-tout » couvrant les besoins de tous les tests, mais chaque test individuel n'en exploite qu'une petite partie."
---
# Fixture générale

> Une configuration partagée construit une grande fixture « fourre-tout » couvrant les besoins de tous les tests, mais chaque test individuel n'en exploite qu'une petite partie.

## Signs and Symptoms

Un unique `beforeEach`/`setUp` (ou une configuration partagée au niveau du module) construit de nombreux objets, insère de nombreux enregistrements et met en place plusieurs mocks — et un test donné n'en touche qu'un ou deux. L'heuristique de détection issue du catalogue des odeurs de test est simple : _tous les champs créés dans la configuration ne sont pas utilisés par toutes les méthodes de test_.

Comment la reconnaître :

* Le bloc de configuration est long et grossit à chaque ajout d'un nouveau test.
* Pour comprendre un seul test, vous devez remonter et reconstituer par rétro-ingénierie quelles parties de la fixture lui importent réellement.
* De nombreux tests échouent ou doivent être modifiés lorsque vous changez un objet partagé dont la plupart d'entre eux ne se soucient même pas.
* La même fixture sert, depuis un seul endroit, des scénarios radicalement différents (création, validation, paiement, permissions).

```js
// Odeur : une seule fixture pour tout ; chaque test n'en utilise qu'une infime partie
let user, admin, product, cart, order, paymentGateway, shippingZone;

beforeEach(() => {
  user            = createUser({ name: 'Ada' });
  admin           = createAdmin();
  product         = createProduct({ price: 10 });
  cart            = createCart(user, [product]);
  order           = createOrder(cart);
  paymentGateway  = mockGateway();
  shippingZone    = createZone('EU');
});

test('cart subtotal sums its line items', () => {
  // n'a besoin que de cart + product, mais paie quand même pour admin, order, gateway, zone…
  expect(cart.subtotal()).toBe(10);
});

```

## Reasons for the Problem

**Pourquoi cela arrive**

* **DRY appliqué de manière trop agressive.** La configuration est regroupée dans un unique hook partagé pour « éviter la duplication », si bien que les besoins de chaque nouveau test viennent se greffer sur la même fixture.
* **Croissance organique.** La fixture accumule des objets au fil de l'ajout des tests, et personne ne supprime ce que les anciens tests n'utilisent plus.
* **Inertie de la configuration implicite.** Une fois qu'un gros `beforeEach` existe, ajouter une ligne de plus est plus facile que de créer une fixture ciblée pour le nouveau cas.

**Pourquoi c'est nuisible**

* **Lisibilité / tests en tant que documentation.** Un test devrait se lire comme une relation claire de cause à effet. Lorsque l'essentiel de la fixture n'est que du bruit hors de propos, le lecteur ne peut pas distinguer ce qui détermine réellement le résultat. L'antidote de Meszaros, la _Minimal Fixture_ (fixture minimale), existe précisément parce qu'un test utilisant la plus petite fixture possible est toujours plus facile à comprendre.
* **Maintenabilité.** Une modification exigée par un seul test (par exemple ajouter un nouveau champ obligatoire à `product`) force à modifier une configuration partagée par tous les tests, ce qui provoque des ruptures en cascade et couple entre eux des tests sans rapport.
* **Fiabilité.** Un état de fixture partagé et mutable laisse les tests s'influencer mutuellement et rend les échecs difficiles à localiser — une source classique d'instabilité dépendant de l'ordre d'exécution.
* **Rapidité.** Chaque test paie le coût intégral de la construction de toute la fixture. La fixture générale figure parmi les causes des Slow Tests (tests lents).
* **Fausse confiance.** Les tests deviennent surspécifiés vis-à-vis de détails accessoires de la fixture, de sorte qu'ils réussissent ou échouent pour des raisons sans rapport avec le comportement testé.

## Treatment

Visez une **Minimal Fixture** (fixture minimale) : chaque test ne met en place que ce dont il a besoin, et rien de plus.

1. **Inventoriez l'utilisation.** Pour chaque champ partagé, dressez la liste des tests qui le lisent réellement. Tout ce qui n'est utilisé que par un sous-ensemble est candidat à être sorti de la configuration partagée.
2. **Déplacez la construction vers des Creation Methods / des constructeurs de données de test (Delegated Setup).** Gardez des tests concis sans imposer une fixture géante — chaque test appelle un builder et ne surcharge que les attributs qui le concernent.
3. **Préférez une Fresh Fixture par test** plutôt qu'une fixture partagée à longue durée de vie, ce qui élimine le couplage entre tests et l'état partagé mutable.
4. **Découpez par scénario.** Si des groupes de tests partagent réellement une _petite_ fixture, séparez-les dans des blocs `describe` ciblés (ou des classes de test), chacun avec sa propre configuration minimale.
5. **Ne gardez dans `beforeEach` que ce qui est vraiment commun et réduit ;** déplacez le reste en ligne afin que la cause et l'effet se trouvent juste à côté de l'assertion.

```js
// Avant : fixture générale dans un hook partagé (voir Signes & symptômes)

// Après : configuration minimale et déléguée — chaque test ne construit que ce dont il a besoin
test('cart subtotal sums its line items', () => {
  const cart = aCart().withItem(aProduct().price(10)).build();
  expect(cart.subtotal()).toBe(10);
});

test('checkout charges the payment gateway', () => {
  const gateway = mockGateway();
  const cart    = aCart().withItem(aProduct().price(10)).build();

  checkout(cart, gateway);

  expect(gateway.charge).toHaveBeenCalledWith(10);
});

```

Chaque test se lit désormais de haut en bas comme une histoire autonome, les builders absorbent le code répétitif, et modifier un scénario ne perturbe plus les autres.
