Mocks et Stubs : Techniques Avancées pour Isoler Vos Tests
Jérémie Calos
27 janvier 2025

Introduction : Isoler pour Mieux Tester
Salut les codeurs ! Aujourd'hui, on va parler des mocks et stubs. Vous savez, ces techniques qui permettent d'isoler vos tests en remplaçant les dépendances par des versions factices.
Si vous avez déjà écrit des tests, vous savez à quel point c'est frustrant quand votre test dépend d'une API externe qui est en panne, ou d'une base de données qui n'est pas disponible. C'est là que les mocks et stubs entrent en jeu.
Dans cet article, je vais vous expliquer ce que sont les mocks et stubs, comment les utiliser, et surtout, quand les utiliser. Parce que comme toute technique, il faut savoir l'utiliser à bon escient.
Qu'est-ce qu'un Mock ?
Un mock est un objet factice qui simule le comportement d'un objet réel. Il permet de vérifier les interactions avec cet objet pendant le test.
Caractéristiques d'un mock :
- Il vérifie les interactions (combien de fois une méthode a été appelée, avec quels paramètres)
- Il peut retourner des valeurs prédéfinies
- Il peut lever des exceptions
Exemple avec Jest :
// Mock d'une fonction API
const fetchUser = jest.fn();
// Définir le comportement
fetchUser.mockResolvedValue({ id: 1, name: 'Jérémie' });
// Utiliser dans le test
const user = await fetchUser(1);
expect(user.name).toBe('Jérémie');
// Vérifier les interactions
expect(fetchUser).toHaveBeenCalledWith(1);
expect(fetchUser).toHaveBeenCalledTimes(1);
Qu'est-ce qu'un Stub ?
Un stub est un objet factice qui remplace un objet réel et retourne des valeurs prédéfinies. Contrairement au mock, il ne vérifie pas les interactions.
Caractéristiques d'un stub :
- Il retourne des valeurs prédéfinies
- Il ne vérifie pas les interactions
- Il est plus simple qu'un mock
Exemple avec Jest :
// Stub d'une fonction
const calculateTax = jest.fn(() => 0.20); // Retourne toujours 20%
// Utiliser dans le test
const price = 100;
const tax = calculateTax(price);
const total = price + (price * tax);
expect(total).toBe(120);
Mock vs Stub : Les Différences
| Aspect | Mock | Stub |
|---|---|---|
| Vérifie les interactions | Oui | Non |
| Retourne des valeurs | Oui | Oui |
| Complexité | Plus complexe | Plus simple |
| Quand l'utiliser | Quand vous voulez vérifier les interactions | Quand vous voulez juste remplacer une dépendance |
Les Cas d'Usage
1. Isoler les Tests Unitaires
Le cas d'usage principal des mocks et stubs est d'isoler les tests unitaires. Si votre fonction dépend d'une API externe, d'une base de données, ou d'un autre service, vous pouvez utiliser un mock pour remplacer cette dépendance.
Exemple :
// Fonction à tester
async function getUserEmail(userId) {
const user = await fetchUserFromAPI(userId);
return user.email;
}
// Test avec mock
test('should return user email', async () => {
// Mock de l'API
const fetchUserFromAPI = jest.fn().mockResolvedValue({
id: 1,
email: 'jeremie@example.com'
});
const email = await getUserEmail(1);
expect(email).toBe('jeremie@example.com');
expect(fetchUserFromAPI).toHaveBeenCalledWith(1);
});
2. Tester les Cas d'Erreur
Les mocks permettent de tester facilement les cas d'erreur en simulant des exceptions ou des erreurs.
Exemple :
test('should handle API error', async () => {
const fetchUserFromAPI = jest.fn().mockRejectedValue(
new Error('API unavailable')
);
await expect(getUserEmail(1)).rejects.toThrow('API unavailable');
});
3. Accélérer les Tests
Les mocks permettent d'accélérer les tests en évitant les appels réseau ou les accès à la base de données.
4. Tester les Interactions
Les mocks permettent de vérifier que votre code interagit correctement avec ses dépendances.
Exemple :
test('should send email after user creation', async () => {
const sendEmail = jest.fn();
await createUser({ name: 'Jérémie', email: 'jeremie@example.com' }, sendEmail);
expect(sendEmail).toHaveBeenCalledWith('jeremie@example.com', 'Welcome!');
});
Les Outils Populaires
Jest (JavaScript/TypeScript)
Jest inclut un système de mocks très puissant intégré.
Exemple :
// Mock d'un module
jest.mock('./api', () => ({
fetchUser: jest.fn()
}));
// Mock d'une fonction
const myFunction = jest.fn();
myFunction.mockReturnValue(42);
pytest (Python)
pytest utilise le module unittest.mock pour les mocks.
Exemple :
from unittest.mock import Mock, patch
def test_user_creation():
with patch('api.fetch_user') as mock_fetch:
mock_fetch.return_value = {'id': 1, 'name': 'Jérémie'}
user = get_user(1)
assert user['name'] == 'Jérémie'
mock_fetch.assert_called_once_with(1)
Mockito (Java)
Mockito est la bibliothèque de mocks la plus populaire pour Java.
Exemple :
@Test
void testUserCreation() {
UserRepository mockRepo = mock(UserRepository.class);
when(mockRepo.findById(1)).thenReturn(new User(1, "Jérémie"));
UserService service = new UserService(mockRepo);
User user = service.getUser(1);
assertEquals("Jérémie", user.getName());
verify(mockRepo).findById(1);
}
Les Bonnes Pratiques
1. Utilisez les Mocks avec Modération
Trop de mocks peuvent rendre vos tests fragiles et difficiles à comprendre. Parfois, il vaut mieux utiliser de vraies dépendances légères.
2. Mockez les Dépendances Externes
Mockez les dépendances externes (APIs, bases de données, services), pas les dépendances internes de votre application.
3. Gardez les Mocks Simples
Un mock complexe est difficile à comprendre et à maintenir. Gardez vos mocks simples.
4. Vérifiez les Interactions Importantes
Vérifiez les interactions importantes, mais ne vérifiez pas chaque petit détail. Ça rendrait vos tests fragiles.
5. Utilisez des Fixtures pour les Données
Utilisez des fixtures ou des factories pour créer les données de test plutôt que de les hardcoder dans chaque mock.
Les Pièges à Éviter
Le Piège du Mock Excessif
Ne mockez pas tout. Si vous mockez trop de choses, vos tests ne testent plus vraiment votre code.
Le Piège du Mock Fragile
Un mock fragile casse même quand le code fonctionne. Souvent, c'est parce que le mock dépend de détails d'implémentation.
Le Piège du Mock Inutile
Ne mockez pas ce qui n'a pas besoin d'être mocké. Si une dépendance est rapide et fiable, utilisez-la directement.
Conclusion : Isoler pour Mieux Tester
Les mocks et stubs sont des outils puissants pour isoler vos tests et les rendre plus fiables. Ils permettent de tester votre code sans dépendre d'APIs externes, de bases de données, ou d'autres services.
Comme toute technique, il faut savoir les utiliser à bon escient. Trop de mocks et vos tests deviennent fragiles. Pas assez et vos tests dépendent de choses externes.
Mon conseil ? Commencez par mocker les dépendances externes (APIs, bases de données). Puis, au fur et à mesure, vous verrez ce qui a vraiment besoin d'être mocké et ce qui peut être testé directement.
Et n'oubliez pas : un mock, c'est comme un double au cinéma. Il doit ressembler à l'original, mais il n'a pas besoin d'être parfait. L'important, c'est qu'il permette de tester votre code.
Allez, maintenant c'est à vous de jouer ! Prenez une fonction qui dépend d'une API externe, et créez un mock pour elle. Vous verrez, une fois que vous aurez pris le pli, vous ne pourrez plus vous en passer.