); const buttonElement = screen.getByText(/click me/i); expect(buttonElement).toBeInTheDocument(); });
test('calls onClick prop when clicked', () => { const handleClick = jest.fn(); render(<Button label="Click me" onClick={handleClick} />); const buttonElement = screen.getByText(/click me/i); fireEvent.click(buttonElement); expect(handleClick).toHaveBeenCalledTimes(1); }); ```
## Tester des composants avec des hooks
Voici un exemple de composant utilisant le Hook `useState` :
```jsx // Counter.jsx import React, { useState } from 'react';
function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <button onClick={() => setCount(count - 1)}>Decrement</button> </div> ); }
export default Counter; ```
Et son test :
```jsx // Counter.test.jsx import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import Counter from './Counter';
test('renders the counter with initial value of 0', () => { render(<Counter />); const countElement = screen.getByText(/count: 0/i); expect(countElement).toBeInTheDocument(); });
test('increments the counter when increment button is clicked', () => { render(<Counter />); const incrementButton = screen.getByText(/increment/i); fireEvent.click(incrementButton); const countElement = screen.getByText(/count: 1/i); expect(countElement).toBeInTheDocument(); });
test('decrements the counter when decrement button is clicked', () => { render(<Counter />); const decrementButton = screen.getByText(/decrement/i); fireEvent.click(decrementButton); const countElement = screen.getByText(/count: -1/i); expect(countElement).toBeInTheDocument(); }); ```
## Tester des composants asynchrones
Pour tester des composants qui effectuent des opérations asynchrones (comme des appels API), vous pouvez utiliser les méthodes asynchrones de React Testing Library :
```jsx // UserList.jsx import React, { useEffect, useState } from 'react';
function UserList() { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { fetch('https://jsonplaceholder.typicode.com/users') .then(response => response.json()) .then(data => { setUsers(data); setLoading(false); }) .catch(error => { setError(error.message); setLoading(false); }); }, []); if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error}</p>; return ( <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> ); }
export default UserList; ```
Et son test avec un mock de l'API :
```jsx // UserList.test.jsx import React from 'react'; import { render, screen, waitForElementToBeRemoved } from '@testing-library/react'; import UserList from './UserList';
// Mock de fetch global.fetch = jest.fn();
beforeEach(() => { jest.clearAllMocks(); });
test('renders loading state initially, then user list', async () => { // Mock de la réponse de l'API global.fetch.mockResolvedValueOnce({ json: async () => ([ { id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' } ]) }); render(<UserList />); // Vérifie l'état de chargement initial expect(screen.getByText(/loading/i)).toBeInTheDocument(); // Attend que l'état de chargement disparaisse await waitForElementToBeRemoved(() => screen.queryByText(/loading/i)); // Vérifie que les utilisateurs sont affichés expect(screen.getByText(/john doe/i)).toBeInTheDocument(); expect(screen.getByText(/jane smith/i)).toBeInTheDocument(); }); ```
## Bonnes pratiques de test
1. **Testez le comportement, pas l'implémentation** : Concentrez-vous sur ce que l'utilisateur voit et peut faire, pas sur les détails d'implémentation qui pourraient changer.
2. **Utilisez des sélecteurs accessibles** : Préférez `getByRole`, `getByLabelText` et `getByText` plutôt que des sélecteurs basés sur les IDs ou les classes.
3. **Écrivez des tests maintenables** : Évitez les tests fragiles qui se cassent facilement lors des refactorisations.
4. **Testez les cas d'erreur** : Assurez-vous de tester non seulement le chemin heureux, mais aussi les cas d'erreur.
## Conclusion
Les tests unitaires sont un investissement qui paie sur le long terme. Ils vous permettent de détecter les régressions rapidement et de refactoriser votre code en toute confiance.
Jest et React Testing Library forment une combinaison puissante pour tester vos composants React d'une manière qui se concentre sur le comportement utilisateur plutôt que sur les détails d'implémentation.