Tests
Qualité et maintenance
Les Tests dans React
Les tests sont essentiels pour garantir la qualité et la maintenabilité de vos applications React. Nous allons explorer les différents types de tests et les outils disponibles.
Configuration de base
Create React App inclut déjà Jest et React Testing Library. Pour les projets existants :
npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event
Tests unitaires
Test d’un composant simple
// Button.js
function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>;
}
// Button.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
test('rendre le bouton avec le bon texte', () => {
render(<Button>Cliquez-moi</Button>);
expect(screen.getByText('Cliquez-moi')).toBeInTheDocument();
});
test('appeler onClick quand le bouton est cliqué', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Cliquez-moi</Button>);
fireEvent.click(screen.getByText('Cliquez-moi'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
Test avec des hooks
// useCounter.js
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
return { count, increment, decrement };
}
// useCounter.test.js
import { renderHook, act } from '@testing-library/react-hooks';
import { useCounter } from './useCounter';
test('incrémenter le compteur', () => {
const { result } = renderHook(() => useCounter(0));
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
Tests d’intégration
Test d’un formulaire
// LoginForm.js
function LoginForm({ onSubmit }) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
onSubmit({ email, password });
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit">Se connecter</button>
</form>
);
}
// LoginForm.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import LoginForm from './LoginForm';
test('soumission du formulaire avec les bonnes données', async () => {
const handleSubmit = jest.fn();
render(<LoginForm onSubmit={handleSubmit} />);
fireEvent.change(screen.getByLabelText(/email/i), {
target: { value: 'test@example.com' },
});
fireEvent.change(screen.getByLabelText(/password/i), {
target: { value: 'password123' },
});
fireEvent.click(screen.getByText(/se connecter/i));
expect(handleSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123',
});
});
Tests de composants avec Redux
// Counter.js
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './counterSlice';
function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<p>Compteur : {count}</p>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
</div>
);
}
// Counter.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
import Counter from './Counter';
const createTestStore = (initialState) => {
return configureStore({
reducer: {
counter: counterReducer,
},
preloadedState: initialState,
});
};
test('incrémenter le compteur avec Redux', () => {
const store = createTestStore({ counter: { value: 0 } });
render(
<Provider store={store}>
<Counter />
</Provider>
);
fireEvent.click(screen.getByText('+'));
expect(screen.getByText('Compteur : 1')).toBeInTheDocument();
});
Tests d’accessibilité
// AccessibleButton.js
function AccessibleButton({ onClick, children }) {
return (
<button
onClick={onClick}
aria-label="Action importante"
role="button"
tabIndex={0}
>
{children}
</button>
);
}
// AccessibleButton.test.js
import { render, screen } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import AccessibleButton from './AccessibleButton';
expect.extend(toHaveNoViolations);
test('le bouton est accessible', async () => {
const { container } = render(
<AccessibleButton onClick={() => {}}>Cliquez-moi</AccessibleButton>
);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Bonnes pratiques
-
Organisation des tests :
- Un fichier de test par composant
- Regroupez les tests par fonctionnalité
- Utilisez des descriptions claires
-
Sélection des éléments :
- Préférez les sélecteurs accessibles
- Évitez les sélecteurs basés sur la structure
- Utilisez des attributs data-testid avec modération
-
Couverture des tests :
- Testez les cas d’utilisation principaux
- Incluez les cas d’erreur
- Testez les interactions utilisateur
Exercices pratiques
-
Créez des tests pour un composant de formulaire qui :
- Valide les entrées
- Gère les erreurs
- Soumet les données
-
Créez des tests pour un composant de liste qui :
- Affiche les éléments
- Permet la sélection
- Gère le tri
-
Créez des tests pour un composant de navigation qui :
- Affiche les liens
- Gère la navigation active
- Est accessible
Conclusion
Les tests sont essentiels pour maintenir la qualité de votre code. Dans le prochain chapitre, nous allons explorer le déploiement de vos applications React.
Commentaires
Les commentaires sont alimentés par GitHub Discussions
Connectez-vous avec GitHub pour participer à la discussion