Mocks et Spies dans Vitest
Les mocks et les spies sont des outils essentiels pour isoler le code testé et simuler des comportements spécifiques. Ils vous permettent de tester vos composants indépendamment de leurs dépendances.
Les fonctions de mock (vi.fn)
Une fonction de mock vous permet de remplacer une fonction réelle par une version simulée dont vous pouvez contrôler le comportement.
import { expect, test, vi } from 'vitest'
test('fonctions de mock de base', () => { // Créer une fonction mock const mockFn = vi.fn()
// Appeler la fonction mock mockFn('arg1', 'arg2')
// Vérifier que la fonction a été appelée expect(mockFn).toHaveBeenCalled()
// Vérifier les arguments d'appel expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2')
// Vérifier le nombre d'appels expect(mockFn).toHaveBeenCalledTimes(1)})
Contrôler le comportement des mocks
Vous pouvez définir la valeur de retour d’une fonction mock :
test('valeurs de retour des mocks', () => { // Retourner une valeur spécifique const mockWithReturn = vi.fn().mockReturnValue(42) expect(mockWithReturn()).toBe(42)
// Retourner différentes valeurs à chaque appel const mockWithMultipleReturns = vi.fn() .mockReturnValueOnce('first call') .mockReturnValueOnce('second call') .mockReturnValue('default')
expect(mockWithMultipleReturns()).toBe('first call') expect(mockWithMultipleReturns()).toBe('second call') expect(mockWithMultipleReturns()).toBe('default')
// Implémenter une fonction personnalisée const mockWithImplementation = vi.fn().mockImplementation((a, b) => a + b) expect(mockWithImplementation(1, 2)).toBe(3)})
Les spies (vi.spyOn)
Les spies permettent de surveiller les appels à une fonction existante sans changer son implémentation.
test('utilisation des spies', () => { const user = { getName: () => 'John', getFullName: (lastName) => `John ${lastName}` }
// Créer un spy sur la méthode getName const getSpy = vi.spyOn(user, 'getName')
// L'appel à la méthode fonctionne normalement expect(user.getName()).toBe('John')
// Mais on peut vérifier qu'elle a été appelée expect(getSpy).toHaveBeenCalled()
// On peut aussi modifier le comportement d'un spy getSpy.mockReturnValue('Jane') expect(user.getName()).toBe('Jane')
// Restaurer l'implémentation originale getSpy.mockRestore() expect(user.getName()).toBe('John')})
Mocker des modules entiers
Vitest permet de mocker des modules complets, ce qui est utile pour remplacer des dépendances externes.
import axios from 'axios'
export async function getUsers() { const response = await axios.get('/api/users') return response.data}
// users.test.jsimport { expect, test, vi } from 'vitest'import { getUsers } from './users'import axios from 'axios'
// Mocker le module axiosvi.mock('axios')
test('getUsers fetch les données correctement', async () => { // Configurer le mock d'axios const mockUsers = [{ id: 1, name: 'John' }] axios.get.mockResolvedValue({ data: mockUsers })
// Appeler la fonction à tester const users = await getUsers()
// Vérifier que axios.get a été appelé avec le bon argument expect(axios.get).toHaveBeenCalledWith('/api/users')
// Vérifier que la fonction retourne les données attendues expect(users).toEqual(mockUsers)})
Implémentation personnalisée de modules mockés
Vous pouvez définir une implémentation personnalisée pour un module mocké :
// Mocker le module avec une implémentation personnaliséevi.mock('./utils', () => { return { formatDate: () => '2023-01-01', calculateTotal: vi.fn().mockReturnValue(100) }})
import { formatDate, calculateTotal } from './utils'
test('modules avec implémentation personnalisée', () => { expect(formatDate()).toBe('2023-01-01') expect(calculateTotal()).toBe(100)})
Les timers simulés
Vitest peut simuler les fonctions de temporisation (setTimeout, setInterval) pour éviter d’attendre réellement le temps spécifié.
test('utilisation des timers simulés', () => { // Activer les faux timers vi.useFakeTimers()
const callback = vi.fn()
// Définir un timer setTimeout(callback, 1000)
// Vérifier que le callback n'a pas encore été appelé expect(callback).not.toHaveBeenCalled()
// Avancer dans le temps vi.advanceTimersByTime(500) expect(callback).not.toHaveBeenCalled()
// Avancer jusqu'à ce que le timer soit déclenché vi.advanceTimersByTime(500) expect(callback).toHaveBeenCalledTimes(1)
// Réinitialiser les timers vi.useRealTimers()})
Meilleures pratiques
- Réinitialiser les mocks : Utilisez
vi.resetAllMocks()
dans les hooksbeforeEach
pour réinitialiser l’état des mocks entre les tests. - Restaurer les spies : Utilisez
mockRestore()
pour les spies afin de restaurer les implémentations originales. - Mocker au niveau approprié : Ne mocker que ce qui est nécessaire, en gardant les tests aussi proches que possible du comportement réel.
- Utiliser des assertions claires : Spécifiez les attentes précises concernant les appels de fonctions (arguments, nombre d’appels, etc.).
- Documenter les mocks : Commentez clairement ce que représentent vos mocks pour faciliter la compréhension des tests.
En maîtrisant les mocks et les spies, vous pourrez créer des tests isolés qui se concentrent sur un comportement spécifique sans dépendre de composants externes.