Testez le comportement plutôt que l'implémentation, exploitez les doubles invocations de StrictMode et mesurez les rendus avec Profiler.
Ouvrir cette leçon dans KodokonLa philosophie de Testing Library tient en une phrase : tester ce que l'utilisateur perçoit, jamais les détails d'implémentation. Concrètement, on interroge le DOM via l'arbre d'accessibilité : getByRole d'abord (la requête reine, qui valide au passage vos rôles ARIA), puis getByLabelText, getByText, et getByTestId en tout dernier recours. Pour les interactions, préférez user-event à fireEvent : là où fireEvent.change émet un événement isolé, user.type reproduit la séquence complète (focus, keydown, keypress, input, keyup), exactement comme un vrai clavier. Les variantes findBy* combinent une requête et une attente asynchrone : indispensables après un chargement.
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('ajoute une tache a la liste', async () => {
const user = userEvent.setup();
render(<TodoApp />);
await user.type(
screen.getByRole('textbox'),
'Relire le rapport'
);
await user.click(
screen.getByRole('button', { name: /ajouter/i })
);
expect(
await screen.findByText('Relire le rapport')
).toBeInTheDocument();
});StrictMode est un outil de développement, sans aucun effet en production. En développement, il appelle deux fois vos fonctions de composant, initialiseurs d'état et fonctions passées à setState, pour débusquer les rendus impurs : si deux exécutions donnent des résultats différents, vous avez un effet de bord caché. Depuis React 18, il exécute aussi chaque effet selon le cycle montage, démontage, remontage : un effet dont le nettoyage n'est pas parfaitement symétrique (abonnement non résilié, requête non annulée) se trahit immédiatement. C'est une préparation directe aux fonctionnalités où React démonte puis remonte un écran en préservant son état.
useEffect(() => {
const controller = new AbortController();
fetch('/api/user', { signal: controller.signal })
.then((res) => res.json())
.then(setUser)
.catch(() => {});
return () => controller.abort();
}, []);Pour mesurer, React fournit le composant <Profiler> et son pendant graphique dans les DevTools. Son callback onRender reçoit notamment phase (mount, update ou nested-update), actualDuration (temps réellement passé à rendre le sous-arbre pour ce commit) et baseDuration (estimation du temps de rendu du sous-arbre entier sans aucune mémoïsation). L'écart entre les deux mesure l'efficacité de vos memo et useMemo : un actualDuration très inférieur au baseDuration signifie que la mémoïsation travaille pour vous.
import { Profiler } from 'react';
function App() {
return (
<Profiler
id="Sidebar"
onRender={(id, phase, actualDuration) => {
console.log(id, phase, actualDuration);
}}
>
<Sidebar />
</Profiler>
);
}baseDuration dans le callback du Profiler ?