---
title: "Excessive Mocking"
type: "test-smell"
slug: "excessive-mocking"
url: "http://localhost:3000/fr/test-smells/excessive-mocking.md"
category: "Odeurs de mock"
description: "Un test met en place tant d'objets simulés (mocks) et d'interactions bouchonnées que la configuration des mocks éclipse la vérification réelle : le test finit par exercer les mocks plutôt que le comportement réel."
---
# Excessive Mocking

> Un test met en place tant d'objets simulés (mocks) et d'interactions bouchonnées que la configuration des mocks éclipse la vérification réelle : le test finit par exercer les mocks plutôt que le comportement réel.

## Signs and Symptoms

Un test est surtout de l'_arrange_ : de longs blocs de création de mocks, des lignes `when(...).thenReturn(...)` / `mockReturnValue(...)` et `verify(...)`, avec peu de vrai code testé. Signes révélateurs :

* **Plus de tuyauterie de mocks que d'assertions.** La mise en place fait de nombreuses lignes ; l'« act » est un seul appel ; l'« assert » vérifie que les mocks ont été _appelés_ plutôt qu'un résultat correct.
* **Simuler ce qui vous appartient.** Des objets de domaine, des objets-valeurs ou des collaborateurs de logique pure sont simulés au lieu de ne simuler que les frontières externes (réseau, base de données, horloge, système de fichiers, tiers).
* **Chaînes de mocks / « train wrecks ».** Un mock renvoie un autre mock qui renvoie un autre mock, reflétant le graphe d'appels de production.
* **Vérification d'interaction seule.** Le test vérifie `toHaveBeenCalledWith(...)` sur chaque collaborateur et ne vérifie jamais la valeur renvoyée ni l'état résultant.
* **Fragile au refactoring.** Réordonner des appels internes ou extraire une méthode casse de nombreux tests alors même que le comportement est inchangé.
* **Cinq mocks ou plus rien que pour instancier le SUT** — signe habituel que le SUT lui-même a trop de dépendances.

```js
test('places order', () => {
  const inventory = { check: jest.fn().mockReturnValue(true) };
  const pricing   = { quote: jest.fn().mockReturnValue(42) };
  const tax       = { calc:  jest.fn().mockReturnValue(4.2) };
  const wallet    = { charge: jest.fn().mockReturnValue({ ok: true }) };
  const ledger    = { record: jest.fn() };
  const emailer   = { send: jest.fn() };
  const audit     = { log: jest.fn() };
  const clock     = { now: jest.fn().mockReturnValue(0) };

  const svc = new OrderService(inventory, pricing, tax, wallet, ledger, emailer, audit, clock);
  svc.place(cart);

  // vérifie que les mocks ont été appelés — pas que la commande est correcte
  expect(inventory.check).toHaveBeenCalled();
  expect(pricing.quote).toHaveBeenCalled();
  expect(wallet.charge).toHaveBeenCalledWith(46.2);
  expect(ledger.record).toHaveBeenCalled();
});

```

## Reasons for the Problem

**Pourquoi cela se produit**

* **Le SUT a trop de collaborateurs.** Un excès de mocks est généralement un signal de _conception_ : une classe à faible cohésion et aux nombreuses dépendances oblige chaque test à toutes les mettre en place. Comme le formule la littérature du catalogue : _« si vous devez simuler cinq classes internes juste pour tester une méthode, la méthode a trop de dépendances — c'est un problème de conception, pas de test. »_
* **Habitude / réflexe du « tout simuler ».** Pousser à l'extrême les tests fondés sur les interactions (« école de Londres »), ou simuler par réflexe pour éviter de toucher une base de données ou le réseau, conduit à simuler de la logique pure qui pourrait être testée directement.
* **Objets réels difficiles à construire.** Lorsque les vrais collaborateurs sont pénibles à instancier, un mock paraît plus simple que de corriger le constructeur ou d'ajouter un faux (fake).

**Pourquoi c'est nuisible**

* **Fausse confiance.** Les mocks encodent _vos hypothèses_ sur une dépendance. Si l'implémentation réelle diverge, le test passe quand même — par ex. un bouchon qui prétend que `sum()` ne renvoie que des entiers positifs maintient un test au vert après que la vraie méthode a changé. De tels tests peuvent devenir tautologiques : ils ne vérifient que le fait que les mocks que vous avez écrits se comportent comme les mocks que vous avez écrits. Comme l'avertit la documentation même de Mockito : _« si tout est simulé, testons-nous vraiment le code de production ? »_
* **Fragilité / forte maintenance.** Vérifier des appels précis fait des détails d'implémentation le contrat : des refactorings anodins (ordre des appels, assistants extraits) cassent les tests. C'est l'_Overspecified Software_ / le _Fragile Test_ de Meszaros.
* **Lisibilité médiocre.** Des centaines de lignes de configuration de dépendances enterrent l'unique chose dont parle le test, à la manière d'un _Mystery Guest_ — un relecteur ne peut pas dire quel comportement est réellement vérifié.
* **Bogues d'intégration manqués.** Le vrai câblage entre les composants n'est jamais exercé ; les bogues vivent précisément dans les jointures que les mocks ont remplacées. Une couverture élevée masque une faible qualité.

## Treatment

Traitez un mocking abondant comme un retour d'information, puis réduisez le _besoin_ de simuler :

1. **Corrigez d'abord la conception.** Si vous devez simuler 5 collaborateurs ou plus pour construire le SUT, répartissez les responsabilités ou réduisez les dépendances du constructeur. Moins de dépendances réelles signifie moins de mocks.
2. **Ne simulez qu'aux frontières d'architecture.** Simulez ce que vous _ne possédez pas_ (réseau, base de données, horloge, système de fichiers, API tierces) ; utilisez des **instances réelles** de vos propres objets de domaine et objets-valeurs.
3. **Extrayez un cœur pur (functional core / imperative shell).** Sortez la logique de calcul/décision de la classe à forte charge d'E/S afin de pouvoir la tester avec **zéro mock**, ne laissant qu'une fine coquille qui n'a besoin que d'un ou deux doublures de frontière.
4. **Préférez la vérification d'état à la vérification d'interaction.** Vérifiez la valeur renvoyée ou l'état résultant plutôt que `verify(...)`/`toHaveBeenCalledWith(...)` sur chaque collaborateur. Réservez les vérifications d'interaction au seul effet de bord qui compte vraiment.
5. **Utilisez la doublure la plus simple qui fonctionne.** Remplacez les mocks élaborés par des **bouchons (stubs)** (qui se contentent de renvoyer des valeurs) ou par un unique **faux en mémoire** réutilisable au lieu de re-bouchonner chaque méthode à chaque test.
6. **Faites ressortir le sur-mocking dynamiquement.** Activer le bouchonnage strict de Mockito (le `MockitoExtension`/`MockitoJUnitRunner` par défaut) lève une `UnnecessaryStubbingException` pour les bouchons configurés mais jamais utilisés — un moyen peu coûteux de trouver les mocks dont vous n'aviez pas besoin.

```js
// AVANT : 8 mocks, on vérifie les appels
const pricing = { quote: jest.fn().mockReturnValue(42) };
const tax     = { calc:  jest.fn().mockReturnValue(4.2) };
// + 6 mocks de plus ...
expect(wallet.charge).toHaveBeenCalledWith(46.2);

// APRÈS : logique pure testée directement — aucun mock
expect(totalFor(cart, rates)).toBe(46.2);

// seule la vraie frontière est simulée ; on vérifie l'état, pas les appels
const wallet = new InMemoryWallet({ balance: 100 });
const svc = new OrderService(new InMemoryInventory(cart), wallet);
const order = svc.place(cart);
expect(order.total).toBe(46.2);
expect(wallet.balance).toBe(53.8);

```
