Aller au contenu principal
Version : 29.7

Tester des applications React

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 →

Chez Facebook, nous utilisons Jest pour tester les applications React.

Configuration

Configuration avec Create React App

Si vous débutez avec React, nous recommandons d'utiliser Create React App. Il est prêt à l'emploi et intègre Jest ! Vous n'aurez qu'à ajouter react-test-renderer pour générer des instantanés.

Exécutez

npm install --save-dev react-test-renderer

Configuration sans Create React App

Pour une application existante, installez quelques paquets pour une intégration optimale. Nous utilisons babel-jest et le préréglage Babel react pour transformer le code dans l'environnement de test. Voir aussi utiliser Babel.

Exécutez

npm install --save-dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer

Votre package.json devrait ressembler à ceci (où <current-version> est la dernière version du paquet). Ajoutez les scripts et entrées de configuration Jest :

{
"dependencies": {
"react": "<current-version>",
"react-dom": "<current-version>"
},
"devDependencies": {
"@babel/preset-env": "<current-version>",
"@babel/preset-react": "<current-version>",
"babel-jest": "<current-version>",
"jest": "<current-version>",
"react-test-renderer": "<current-version>"
},
"scripts": {
"test": "jest"
}
}
babel.config.js
module.exports = {
presets: [
'@babel/preset-env',
['@babel/preset-react', {runtime: 'automatic'}],
],
};

Et voilà, vous êtes prêt !

Test par instantané (Snapshot Testing)

Créons un test par instantané pour un composant Link affichant des hyperliens :

Link.js
import {useState} from 'react';

const STATUS = {
HOVERED: 'hovered',
NORMAL: 'normal',
};

export default function Link({page, children}) {
const [status, setStatus] = useState(STATUS.NORMAL);

const onMouseEnter = () => {
setStatus(STATUS.HOVERED);
};

const onMouseLeave = () => {
setStatus(STATUS.NORMAL);
};

return (
<a
className={status}
href={page || '#'}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
{children}
</a>
);
}
note

Les exemples utilisent des composants fonction, mais les composants classe se testent de même. Voir React : Composants fonction et classe. Rappel : pour les composants classe, Jest doit tester les props plutôt que les méthodes directement.

Utilisons maintenant le moteur de rendu de test de React et la fonctionnalité d'instantané de Jest pour interagir avec le composant, capturer le rendu et créer un fichier d'instantané :

Link.test.js
import renderer from 'react-test-renderer';
import Link from '../Link';

it('changes the class when hovered', () => {
const component = renderer.create(
<Link page="http://www.facebook.com">Facebook</Link>,
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();

// manually trigger the callback
renderer.act(() => {
tree.props.onMouseEnter();
});
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();

// manually trigger the callback
renderer.act(() => {
tree.props.onMouseLeave();
});
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

Lorsque vous exécutez yarn test ou jest, cela générera un fichier de sortie similaire à ceci :

__tests__/__snapshots__/Link.test.js.snap
exports[`changes the class when hovered 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;

exports[`changes the class when hovered 2`] = `
<a
className="hovered"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;

exports[`changes the class when hovered 3`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;

Lors des prochains tests, le rendu sera comparé à l'instantané précédent. L'instantané doit être versionné avec les modifications de code. Si un test échoue, vérifiez si le changement est intentionnel ou non. Si le changement est voulu, exécutez jest -u pour écraser l'instantané existant.

Le code de cet exemple est disponible dans examples/snapshot.

Test par instantané avec mocks, Enzyme et React 16+

Attention avec Enzyme et React 16+ lors des tests par instantané. Si vous simulez un module ainsi :

jest.mock('../SomeDirectory/SomeComponent', () => 'SomeComponent');

Des avertissements apparaîtront dans la console :

Warning: <SomeComponent /> is using uppercase HTML. Always use lowercase HTML tags in React.

# Or:
Warning: The tag <SomeComponent> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.

React 16 déclenche ces avertissements via sa vérification des types d'éléments, que le module mocké échoue. Vos options :

  1. Rendre en texte. Vous ne verrez pas les props du composant mocké dans l'instantané, mais c'est simple :

    jest.mock('./SomeComponent', () => () => 'SomeComponent');
  2. Rendre en élément personnalisé. Les "éléments personnalisés" du DOM ne génèrent pas d'avertissements (minuscules avec tiret) :

    jest.mock('./Widget', () => () => <mock-widget />);
  3. Utiliser react-test-renderer. Ce moteur de rendu ignore les types d'éléments et accepte SomeComponent. Vérifiez les instantanés avec lui et le comportement des composants avec Enzyme.

  4. Désactiver tous les avertissements (dans votre fichier de configuration Jest) :

    jest.mock('fbjs/lib/warning', () => require('fbjs/lib/emptyFunction'));

    Cette option est déconseillée car elle supprime des avertissements utiles. Toutefois, lors de tests de composants React Native rendus dans le DOM, beaucoup d'avertissements sont non pertinents. Alternative : intercepter console.warn pour supprimer des avertissements spécifiques.

Test du DOM

Pour vérifier et manipuler vos composants rendus, utilisez @testing-library/react, Enzyme ou les TestUtils de React. L'exemple suivant utilise @testing-library/react.

@testing-library/react

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

Implémentons une case à cocher qui alterne entre deux étiquettes :

CheckboxWithLabel.js
import {useState} from 'react';

export default function CheckboxWithLabel({labelOn, labelOff}) {
const [isChecked, setIsChecked] = useState(false);

const onChange = () => {
setIsChecked(!isChecked);
};

return (
<label>
<input type="checkbox" checked={isChecked} onChange={onChange} />
{isChecked ? labelOn : labelOff}
</label>
);
}
__tests__/CheckboxWithLabel-test.js
import {cleanup, fireEvent, render} from '@testing-library/react';
import CheckboxWithLabel from '../CheckboxWithLabel';

// Note: running cleanup afterEach is done automatically for you in @testing-library/react@9.0.0 or higher
// unmount and cleanup DOM after the test is finished.
afterEach(cleanup);

it('CheckboxWithLabel changes the text after click', () => {
const {queryByLabelText, getByLabelText} = render(
<CheckboxWithLabel labelOn="On" labelOff="Off" />,
);

expect(queryByLabelText(/off/i)).toBeTruthy();

fireEvent.click(getByLabelText(/off/i));

expect(queryByLabelText(/on/i)).toBeTruthy();
});

Le code de cet exemple est disponible sur examples/react-testing-library.

Transformateurs personnalisés

Si vous avez besoin de fonctionnalités plus avancées, vous pouvez également créer votre propre transformateur. Au lieu d'utiliser babel-jest, voici un exemple utilisant @babel/core :

custom-transformer.js
'use strict';

const {transform} = require('@babel/core');
const jestPreset = require('babel-preset-jest');

module.exports = {
process(src, filename) {
const result = transform(src, {
filename,
presets: [jestPreset],
});

return result || src;
},
};

N'oubliez pas d'installer les paquets @babel/core et babel-preset-jest pour que cet exemple fonctionne.

Pour que cela fonctionne avec Jest, vous devez mettre à jour votre configuration Jest avec ceci : "transform": {"\\.js$": "path/to/custom-transformer.js"}.

Si vous souhaitez créer un transformateur avec le support de Babel, vous pouvez également utiliser babel-jest pour en composer un et passer vos options de configuration personnalisées :

const babelJest = require('babel-jest');

module.exports = babelJest.createTransformer({
presets: ['my-custom-preset'],
});

Consultez la documentation dédiée pour plus de détails.