Tester des applications React
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
- Yarn
- pnpm
- Bun
npm install --save-dev react-test-renderer
yarn add --dev react-test-renderer
pnpm add --save-dev react-test-renderer
bun add --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
- Yarn
- pnpm
- Bun
npm install --save-dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
yarn add --dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
pnpm add --save-dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
bun add --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"
}
}
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 :
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>
);
}
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é :
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 :
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 :
-
Rendre en texte. Vous ne verrez pas les props du composant mocké dans l'instantané, mais c'est simple :
jest.mock('./SomeComponent', () => () => 'SomeComponent'); -
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 />); -
Utiliser
react-test-renderer. Ce moteur de rendu ignore les types d'éléments et accepteSomeComponent. Vérifiez les instantanés avec lui et le comportement des composants avec Enzyme. -
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
- Yarn
- pnpm
- Bun
npm install --save-dev @testing-library/react
yarn add --dev @testing-library/react
pnpm add --save-dev @testing-library/react
bun add --dev @testing-library/react
Implémentons une case à cocher qui alterne entre deux étiquettes :
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>
);
}
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 :
'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.