Aller au contenu principal

Migrer depuis Enzyme

[Traduction Bêta Non Officielle]

Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →

Cette page s'adresse aux développeurs ayant une expérience d'Enzyme et cherchant à comprendre comment migrer vers React Testing Library. Elle ne détaille pas exhaustivement la migration de tous les types de tests, mais fournit des informations utiles pour ceux qui comparent Enzyme avec React Testing Library.

Qu'est-ce que React Testing Library ?

React Testing Library fait partie d'un projet open source nommé Testing Library. Ce projet contient plusieurs autres outils et bibliothèques utiles pour écrire des tests plus concis et pertinents. En plus de React Testing Library, voici quelques-unes des bibliothèques du projet qui peuvent vous aider :

  • @testing-library/jest-dom : jest-dom fournit un ensemble d'assertions personnalisées pour Jest. Elles rendent vos tests plus déclaratifs, plus faciles à lire et à maintenir.

  • @testing-library/user-event: user-event tente de simuler les événements réels du navigateur lorsqu'un utilisateur interagit avec des éléments de la page. Par exemple, userEvent.click(checkbox) modifierait l'état de la case à cocher.

Pourquoi utiliser React Testing Library ?

Enzyme est une bibliothèque de tests puissante, et ses contributeurs ont beaucoup apporté à la communauté JavaScript. D'ailleurs, nombre des mainteneurs de React Testing Library ont utilisé et contribué à Enzyme pendant des années avant de développer React Testing Library. Nous tenons donc à remercier les contributeurs d'Enzyme !

L'objectif principal de React Testing Library est d'accroître la confiance dans vos tests en vérifiant vos composants comme le ferait un utilisateur. Les utilisateurs ne se soucient pas des mécanismes internes, seulement du rendu et des interactions. Plutôt que d'accéder aux API internes des composants ou à leur state, vous gagnerez en confiance en écrivant des tests basés sur le rendu final.

React Testing Library résout un problème rencontré par de nombreux développeurs avec Enzyme, qui permet (et encourage) à tester les détails d'implémentation. De tels tests empêchent finalement de modifier et refactoriser le composant sans changer ses tests. En conséquence, les tests ralentissent la vitesse de développement et la productivité. Chaque petite modification peut nécessiter de réécrire une partie des tests, même si le rendu n'est pas affecté.

Réécrire vos tests avec React Testing Library en vaut la peine car vous remplacerez des tests qui vous ralentissent par des tests qui renforcent votre confiance et augmentent votre productivité à long terme.

Comment migrer d'Enzyme vers React Testing Library ?

Pour une migration réussie, nous recommandons une approche progressive en exécutant les deux bibliothèques côte à côte dans la même application, en migrant vos tests Enzyme vers React Testing Library un par un. Cela permet de migrer même des applications complexes sans perturber l'activité, car le travail peut être collaboratif et étalé dans le temps.

Installer React Testing Library

Commencez par installer React Testing Library et la bibliothèque d'aide jest-dom (consultez cette page pour le guide complet d'installation).

npm install --save-dev @testing-library/react @testing-library/jest-dom

Importer React Testing Library dans vos tests

Si vous utilisez Jest (d'autres frameworks sont possibles), importez simplement les modules suivants dans votre fichier de test :

// import React so you can use JSX (React.createElement) in your test
import React from 'react'

/**
* render: lets us render the component as React would
* screen: a utility for finding elements the same way the user does
*/
import {render, screen} from '@testing-library/react'

La structure des tests peut rester identique à celle utilisée avec Enzyme :

test('test title', () => {
// Your tests come here...
})

Remarque : vous pouvez aussi utiliser describe et it avec React Testing Library. React Testing Library ne remplace pas Jest, seulement Enzyme. Nous recommandons test car cela aide à Éviter l'imbrication dans les tests.

Exemples basiques de migration d'Enzyme vers React Testing Library

Notez qu'il n'existe pas de correspondance directe entre les fonctionnalités d'Enzyme et de React Testing Library. De nombreuses fonctionnalités d'Enzyme produisent de toute façon des tests inefficaces, donc certaines habitudes devront être abandonnées (plus besoin de variable wrapper ou d'appels à wrapper.update(), etc.).

React Testing Library propose des requêtes utiles pour accéder aux éléments de vos composants et leurs propriétés. Nous montrerons des tests Enzyme typiques ainsi que leurs alternatives avec React Testing Library.

Prenons un composant Welcome affichant un message de bienvenue. Nous examinerons les tests Enzyme et React Testing Library pour comprendre comment tester ce composant :

Composant React

Ce composant récupère un name via les props et affiche un message de bienvenue dans un élément h1. Il contient aussi un champ texte modifiable par l'utilisateur pour changer le nom, avec mise à jour dynamique du message. Voir la version live sur CodeSandbox.

const Welcome = props => {
const [values, setValues] = useState({
firstName: props.firstName,
lastName: props.lastName,
})

const handleChange = event => {
setValues({...values, [event.target.name]: event.target.value})
}

return (
<div>
<h1>
Welcome, {values.firstName} {values.lastName}
</h1>

<form name="userName">
<label>
First Name
<input
value={values.firstName}
name="firstName"
onChange={handleChange}
/>
</label>

<label>
Last Name
<input
value={values.lastName}
name="lastName"
onChange={handleChange}
/>
</label>
</form>
</div>
)
}

export default Welcome

Test 1 : Rendre le composant et vérifier la valeur du h1

Test Enzyme

test('has correct welcome text', () => {
const wrapper = shallow(<Welcome firstName="John" lastName="Doe" />)
expect(wrapper.find('h1').text()).toEqual('Welcome, John Doe')
})

Test React Testing Library

test('has correct welcome text', () => {
render(<Welcome firstName="John" lastName="Doe" />)
expect(screen.getByRole('heading')).toHaveTextContent('Welcome, John Doe')
})

Comme vous le voyez, les tests sont assez similaires. Le rendu shallow d'Enzyme ne rend pas les sous-composants, donc la méthode render de React Testing Library est plus proche du mount d'Enzyme.

Avec React Testing Library, inutile d'assigner le résultat de render à une variable (comme wrapper). Vous accédez simplement au rendu via les fonctions de l'objet screen. Autre avantage : React Testing Library nettoie automatiquement l'environnement après chaque test, sans besoin d'appeler cleanup dans afterEach ou beforeEach.

Vous remarquerez aussi getByRole avec l'argument 'heading'. 'heading' est le rôle d'accessibilité de l'élément h1. Vous en apprendrez plus dans la documentation des requêtes. Ce que les développeurs apprécient rapidement avec Testing Library, c'est comment elle encourage à créer des applications plus accessibles (car si c'est inaccessible, c'est plus dur à tester).

Test 2 : Les champs texte doivent avoir la bonne valeur

Dans le composant ci-dessus, les valeurs des champs sont initialisées avec props.firstName et props.lastName. Nous devons vérifier leur exactitude.

Enzyme

test('has correct input value', () => {
const wrapper = shallow(<Welcome firstName="John" lastName="Doe" />)
expect(wrapper.find('input[name="firstName"]').value).toEqual('John')
expect(wrapper.find('input[name="lastName"]').value).toEqual('Doe')
})

React Testing Library

test('has correct input value', () => {
render(<Welcome firstName="John" lastName="Doe" />)
expect(screen.getByRole('form')).toHaveFormValues({
firstName: 'John',
lastName: 'Doe',
})
})

Parfait ! C'est simple et pratique, et les tests sont suffisamment clairs sans explication supplémentaire. Vous remarquerez peut-être que le <form> possède un attribut role="form" - à quoi sert-il ?

role est un attribut d'accessibilité recommandé pour améliorer votre application pour les personnes handicapées. Certains éléments ont des valeurs role par défaut et vous n'avez pas besoin de les définir, mais d'autres comme <div> n'ont pas de valeur role par défaut. Vous pouvez utiliser différentes approches pour accéder à un élément <div>, mais nous recommandons d'utiliser leur role implicite pour garantir l'accessibilité aux personnes handicapées et utilisateurs de lecteurs d'écran. Cette section de la documentation des requêtes vous aidera à mieux comprendre ces concepts.

Un élément <form> doit avoir un attribut name pour bénéficier d'un role implicite 'form' (conformément aux spécifications).

React Testing Library vise à tester les composants comme les utilisateurs les utilisent. Les utilisateurs perçoivent les boutons, titres, formulaires et autres éléments par leur rôle, non par leur id, class ou nom de balise. Par conséquent, lorsque vous utilisez React Testing Library, vous devriez éviter d'accéder au DOM via l'API document.querySelector. (Vous pouvez l'utiliser dans vos tests, mais ce n'est pas recommandé pour les raisons évoquées ici.)

React Testing Library expose des API de requête pratiques qui vous aident à accéder efficacement aux éléments des composants. Vous pouvez consulter la liste des requêtes disponibles ici. Si vous n'êtes pas certain de la requête à utiliser dans une situation donnée, nous avons une excellente page qui explique quelle requête choisir – allez y jeter un œil !

Si vous avez encore des questions sur le choix des requêtes de React Testing Library, consultez testing-playground.com et l'extension Chrome Testing Playground conçue pour aider les développeurs à trouver la meilleure requête lors de l'écriture de tests. Elle vous aide également à trouver les meilleures requêtes pour sélectionner des éléments. Elle permet d'inspecter les hiérarchies d'éléments dans les Chrome Developer Tools et vous fournit des suggestions de sélection, tout en encourageant les bonnes pratiques de test.

Utilisation de act() et wrapper.update()

Lors du test de code asynchrone avec Enzyme, vous devez généralement appeler act() pour exécuter vos tests correctement. Avec React Testing Library, vous n'avez généralement pas besoin d'appeler explicitement act() car il encapsule les appels d'API avec act() par défaut.

update() synchronise l'instantané de l'arborescence de composants Enzyme avec l'arborescence React, vous pouvez donc rencontrer wrapper.update() dans les tests Enzyme. React Testing Library n'a pas (ni n'a besoin) d'une méthode similaire, ce qui est avantageux car vous avez moins de choses à gérer !

Simulation d'événements utilisateur

Il existe deux façons de simuler des événements utilisateur avec React Testing Library. La première consiste à utiliser la bibliothèque user-event, et l'autre à utiliser fireEvent qui est incluse dans React Testing Library. user-event est en réalité construit au-dessus de fireEvent (qui appelle simplement dispatchEvent sur l'élément donné). user-event est généralement recommandé car il garantit que tous les événements sont déclenchés dans le bon ordre pour des interactions utilisateur typiques. Cela permet de s'assurer que vos tests reflètent fidèlement l'utilisation réelle de votre logiciel.

Pour utiliser le module @testing-library/user-event, installez-le d'abord :

npm install --save-dev @testing-library/user-event @testing-library/dom

Vous pouvez maintenant l'importer dans votre test :

import userEvent from '@testing-library/user-event'

Pour illustrer l'utilisation de la bibliothèque user-event, imaginez un composant Checkbox qui affiche une case à cocher et son libellé associé. Nous voulons simuler le clic utilisateur sur la case à cocher :

import React from 'react'

const Checkbox = () => {
return (
<div>
<label htmlFor="checkbox">Check</label>
<input id="checkbox" type="checkbox" />
</div>
)
}

export default Checkbox

Nous voulons tester que lorsqu'un utilisateur clique sur le libellé associé à la case à cocher, la propriété "checked" de l'input est correctement définie. Voyons comment écrire un test pour ce cas :

test('handles click correctly', async () => {
render(<Checkbox />)
const user = userEvent.setup()

// You can also call this method directly on userEvent,
// but using the methods from `.setup()` is recommended.
await user.click(screen.getByText('Check'))

expect(screen.getByLabelText('Check')).toBeChecked()
})

Parfait !

Déclenchement de méthodes de classe dans les tests (wrapper.instance())

Comme nous l'avons déjà mentionné, nous déconseillons de tester les détails d'implémentation et les éléments que les utilisateurs ne verront pas. Nous visons à tester et interagir avec le composant de la manière dont nos utilisateurs le feraient.

Si votre test utilise `instance()` ou `state()`, sachez que vous testez des éléments que l'utilisateur ne pourrait pas connaître ni même lui importer, éloignant vos tests de leur objectif : garantir que le système fonctionnera lorsque l'utilisateur interagira avec. — Kent C. Dodds

Si vous ne savez pas comment tester un comportement interne à votre composant, prenez du recul et demandez-vous : "Quelle action utilisateur déclencherait ce code ?" Puis concevez votre test pour reproduire cette interaction.

Comment effectuer un rendu shallow d'un composant ?

En règle générale, évitez de simuler des composants. Si nécessaire, privilégiez les fonctions de simulation de Jest. Pour plus de détails, consultez notre FAQ.