Glossaire · Fondamentaux du test
Doublure de test (test double)
En anglais : Test double
Également appelé : doublures de test, test doubles, stub, mock
Définition
Tout objet qui remplace une vraie dépendance dans un test, comme un dummy, un stub, un fake, un spy ou un mock, pour que le code testé s'exécute vite et de façon prévisible.
Une doublure de test (test double) remplace une vraie dépendance, comme une passerelle de paiement, une base de données, une horloge ou un service d’e-mail, par quelque chose que le test contrôle. Le nom, popularisé par Gerard Meszaros, vient de la doublure cascadeur au cinéma. Les doublures rendent les tests rapides et déterministes, et permettent de simuler des situations difficiles à provoquer en vrai, comme un timeout ou une carte refusée.
Les cinq types
| Type | Ce qu’il fait | Exemple |
|---|---|---|
| Dummy | Passé uniquement pour remplir un paramètre, jamais utilisé | Un logger null ou vide exigé par la méthode mais que ce test n’utilise jamais |
| Stub | Renvoie des réponses préparées aux appels | rates.getRate("EUR") renvoie toujours 0.92 |
| Spy | Un stub qui enregistre aussi comment il a été appelé | Enregistre que send() a été appelé une fois avec une adresse donnée |
| Mock | Préprogrammé avec des attentes ; le test vérifie les interactions | Vérifier que gateway.charge(4999) a été appelé exactement une fois |
| Fake | Une implémentation fonctionnelle mais simplifiée | Un repository en mémoire reposant sur une HashMap |
Dans le langage courant, « mock » désigne tous ces types, et des bibliothèques comme Mockito créent des objets capables de jouer le rôle de stubs, de spies ou de mocks. Connaître la distinction reste utile, en particulier stub ou mock : un stub fournit des entrées au code testé (vérification d’état), tandis qu’un mock contrôle les sorties que le code envoie à ses dépendances (vérification de comportement).
Exemple (JUnit 5 avec Mockito)
@Test
void declinedCardDoesNotSendConfirmation() {
PaymentGateway gateway = mock(PaymentGateway.class);
EmailService email = mock(EmailService.class);
when(gateway.charge(4999)).thenReturn(PaymentResult.DECLINED); // stub
var checkout = new Checkout(gateway, email);
var result = checkout.placeOrder(4999);
assertEquals(OrderStatus.PAYMENT_FAILED, result.status());
verify(email, never()).sendConfirmation(any()); // mock-style verification
}
(Les imports statiques de org.mockito.Mockito, org.mockito.ArgumentMatchers et org.junit.jupiter.api.Assertions sont omis.)
Erreurs fréquentes
- Tout mocker. Les tests qui vérifient chaque appel interne cassent à chaque refactoring et peuvent passer alors que l’intégration réelle est cassée. Mockez aux frontières de l’architecture ; à l’intérieur, préférez les vrais objets ou des fakes.
- Des doublures qui s’éloignent de la réalité. Une API stubbée qui renvoie un champ que le vrai service a renommé il y a des mois donne une fausse confiance. Les tests de contrat et quelques vrais tests d’intégration gardent les doublures honnêtes.
- Mocker des types qui ne vous appartiennent pas. Encapsulez les clients tiers dans votre propre interface et doublez plutôt celle-ci.