
Requêtes de sélection (Queries : getBy, findBy, queryBy...)
Apprenez à utiliser efficacement les différentes requêtes (getBy, queryBy, findBy...) de React Testing Library pour sélectionner des éléments DOM dans vos tests React.
Le coeur de l'interaction : Trouver les éléments
Une fois votre composant rendu avec la fonction `render`, l'étape suivante consiste presque toujours à trouver des éléments spécifiques dans le DOM résultant. Vous en avez besoin soit pour simuler des interactions utilisateur (comme cliquer sur un bouton trouvé), soit pour faire des assertions sur leur état ou leur présence (comme vérifier qu'un message d'erreur s'affiche). React Testing Library (RTL) fournit un ensemble puissant et sémantique de fonctions pour accomplir cette tâche : les Requêtes (Queries).
Conformément à la philosophie de RTL, ces requêtes sont conçues pour vous aider à trouver des éléments de la manière dont un utilisateur le ferait, en privilégiant les attributs d'accessibilité et le contenu visible plutôt que les détails de l'implémentation (comme les noms de classes CSS ou la structure interne du DOM). Maîtriser ces requêtes est essentiel pour écrire des tests efficaces et robustes.
Les trois familles de requêtes : getBy, queryBy, findBy
Les requêtes de RTL se déclinent en trois variantes principales, différenciées par leur préfixe, qui déterminent leur comportement lorsqu'un ou plusieurs éléments sont trouvés ou non :
getBy*: Cette famille de requêtes est utilisée lorsque vous vous attendez absolument à ce que l'élément soit présent dans le DOM au moment de l'appel.- Si aucun élément correspondant n'est trouvé, `getBy*` lève une erreur immédiate, faisant échouer le test.
- Si plus d'un élément correspondant est trouvé, `getBy*` lève également une erreur immédiate.
- Si exactement un élément est trouvé, il le retourne.
- Cas d'usage typique : Vérifier la présence initiale d'un élément qui doit exister (un titre, un bouton principal).
queryBy*: Cette famille est utilisée pour rechercher un élément qui pourrait ne pas être présent, et dont l'absence n'est pas une erreur en soi.- Si aucun élément correspondant n'est trouvé, `queryBy*` retourne `null` (et ne lève pas d'erreur).
- Si plus d'un élément correspondant est trouvé, `queryBy*` lève une erreur immédiate (comme `getBy*`).
- Si exactement un élément est trouvé, il le retourne.
- Cas d'usage typique : Vérifier qu'un élément n'est pas présent à l'écran (ex: `expect(screen.queryByText('Erreur')).toBeNull();`).
findBy*: Cette famille est utilisée pour rechercher des éléments qui apparaissent de manière asynchrone (après un chargement de données, une minuterie, une animation, etc.).- `findBy*` retourne une Promesse.
- La promesse se résout lorsqu'un élément correspondant est trouvé. Elle réessaie de trouver l'élément à intervalles réguliers pendant un temps défini (par défaut 1000ms).
- La promesse est rejetée si aucun élément n'est trouvé après le délai d'attente, ou si plus d'un élément est trouvé.
- Cas d'usage typique : Attendre qu'un élément apparaisse après un appel API (ex: `const articleTitle = await screen.findByText('Titre chargé');`).
Ces trois préfixes (`get`, `query`, `find`) se combinent ensuite avec différents suffixes qui spécifient comment rechercher l'élément (par rôle, par texte, etc.).
Les sélecteurs (Suffixes) : Comment chercher ?
RTL propose plusieurs types de sélecteurs (suffixes des requêtes), avec un ordre de priorité recommandé pour favoriser l'accessibilité et la robustesse :
ByRole: (Ex: `getByRole('button', { name: /submit/i })`) - Le plus recommandé. Trouve les éléments par leur rôle ARIA implicite (pour les éléments HTML sémantiques comme `ByLabelText: (Ex: `getByLabelText('Username')`) - Très utile pour les éléments de formulaire (``, `ByPlaceholderText: (Ex: `getByPlaceholderText('Enter your email')`) - Trouve un élément de formulaire par son attribut `placeholder`.ByText: (Ex: `getByText('Hello World')`, `queryByText(/error/i)`) - Trouve des éléments non interactifs (ou interactifs) par leur contenu textuel. Peut prendre une chaîne exacte, une expression régulière ou une fonction.ByDisplayValue: (Ex: `findByDisplayValue('Initial Value')`) - Trouve un élément de formulaire (`input`, `textarea`, `select`) par la valeur actuellement affichée à l'utilisateur.ByAltText: (Ex: `getByAltText('Logo de l'entreprise')`) - Trouve une image (``) ou un `area` par son texte alternatif (`alt`).
ByTitle: (Ex: `getByTitle('Close')`) - Trouve un élément par son attribut `title` (souvent utilisé pour les infobulles).ByTestId: (Ex: `getByTestId('specific-component-identifier')`) - Trouve un élément par l'attribut `data-testid`. A utiliser en dernier recours, lorsque les autres requêtes sémantiques ne sont pas pratiques ou possibles. C'est un "escape hatch" qui couple le test à un attribut spécifique ajouté uniquement pour le test.
Gestion des éléments multiples : `*AllBy*`
Chacune des trois familles de requêtes (`getBy*`, `queryBy*`, `findBy*`) possède une variante pour trouver plusieurs éléments : `getAllBy*`, `queryAllBy*`, et `findAllBy*`. Ces variantes retournent toujours un tableau d'éléments (ou une promesse résolvant en un tableau pour `findAllBy*`).
getAllBy*: Lève une erreur si aucun élément n'est trouvé. Retourne un tableau contenant un ou plusieurs éléments sinon. Utile quand vous vous attendez à trouver une liste d'éléments.queryAllBy*: Ne lève jamais d'erreur basée sur le nombre d'éléments trouvés. Retourne un tableau des éléments trouvés, ou un tableau vide (`[]`) si aucun n'est trouvé. Utile pour vérifier qu'une liste est vide ou pour manipuler une liste potentiellement vide.findAllBy*: Retourne une Promesse qui se résout avec un tableau d'éléments lorsque au moins un est trouvé (après potentiellement plusieurs tentatives). Rejette si aucun élément n'est trouvé après le délai.
Exemple : `const listItems = screen.getAllByRole('listitem'); expect(listItems).toHaveLength(3);`
Choisir la bonne requête : Un guide rapide
- Besoin de vérifier qu'un élément est présent (sinon erreur) ? Utilisez
getBy*. - Besoin de vérifier qu'un élément n'est pas présent (l'absence est ok) ? Utilisez
queryBy*et vérifiez si le résultat est `null`. - Besoin d'attendre qu'un élément apparaisse (async) ? Utilisez
findBy*(avec `await`). - Besoin de travailler avec une liste d'éléments ? Utilisez les variantes
*AllBy*. - Pour le sélecteur (suffixe), suivez l'ordre de priorité recommandé (
ByRole>ByLabelText> ... >ByTestId).
La maîtrise de ces différentes requêtes vous permettra de cibler précisément les éléments nécessaires à vos tests de manière sémantique et robuste, en accord avec les meilleures pratiques de React Testing Library.