Pruebas en aplicaciones React
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
En Facebook, usamos Jest para probar aplicaciones de React.
Configuración
Configuración con Create React App
Si eres nuevo en React, recomendamos usar Create React App. Está listo para usar y viene con Jest integrado! Solo necesitarás añadir react-test-renderer para renderizar instantáneas.
Ejecuta
- 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
Configuración sin Create React App
Si tienes una aplicación existente, necesitarás instalar algunos paquetes para que todo funcione correctamente. Usamos babel-jest y el preset react de Babel para transformar nuestro código en el entorno de pruebas. También consulta uso de babel.
Ejecuta
- 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
Tu package.json debería verse similar a esto (donde <current-version> es la última versión real del paquete). Añade los scripts y entradas de configuración de 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'}],
],
};
¡Y listo para comenzar!
Pruebas con instantáneas
Creemos una prueba de instantánea para un componente Link que renderiza hipervínculos:
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>
);
}
Los ejemplos usan componentes funcionales, pero los componentes de clase pueden probarse igual. Consulta React: Componentes funcionales y de clase. Recordatorio: con componentes de clase, se espera que Jest pruebe las props directamente, no los métodos.
Ahora usemos el renderizador de pruebas de React y la función de instantáneas de Jest para interactuar con el componente, capturar su salida renderizada y crear un archivo de instantánea:
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();
});
Cuando ejecutes yarn test o jest, se generará un archivo de salida como este:
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>
`;
La próxima vez que ejecutes las pruebas, la salida renderizada se comparará con la instantánea anterior. Esta instantánea debe guardarse junto con los cambios de código. Cuando falle una prueba de instantánea, debes inspeccionar si el cambio es intencional o no. Si el cambio es esperado, puedes ejecutar Jest con jest -u para sobrescribir la instantánea existente.
El código de este ejemplo está disponible en examples/snapshot.
Pruebas de instantáneas con mocks, Enzyme y React 16+
Hay una advertencia sobre las pruebas de instantáneas al usar Enzyme con React 16+. Si simulas un módulo de esta forma:
jest.mock('../SomeDirectory/SomeComponent', () => 'SomeComponent');
Verás advertencias en la consola:
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 dispara estas advertencias por cómo verifica los tipos de elementos, y el módulo simulado falla estas verificaciones. Tus opciones son:
-
Renderizar como texto. Así no verás las props pasadas al componente simulado en la instantánea, pero es sencillo:
jest.mock('./SomeComponent', () => () => 'SomeComponent'); -
Renderizar como elemento personalizado. Los "elementos personalizados" del DOM no se verifican y no deberían generar advertencias. Son en minúsculas y tienen un guión en el nombre.
jest.mock('./Widget', () => () => <mock-widget />); -
Usar
react-test-renderer. El renderizador de pruebas no verifica tipos de elementos y aceptará fácilmente ej.SomeComponent. Puedes verificar instantáneas con este renderizador y el comportamiento del componente por separado con Enzyme. -
Desactivar todas las advertencias (debe hacerse en tu archivo de configuración de Jest):
jest.mock('fbjs/lib/warning', () => require('fbjs/lib/emptyFunction'));Normalmente no debería ser tu opción preferida pues podrías perder advertencias útiles. Sin embargo, en algunos casos, como al probar componentes de react-native que renderizan etiquetas nativas en DOM, muchas advertencias son irrelevantes. Otra opción es interceptar console.warn para suprimir advertencias específicas.
Pruebas de DOM
Si quieres verificar y manipular componentes renderizados, puedes usar @testing-library/react, Enzyme o TestUtils de React. El siguiente ejemplo usa @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
Implementemos una casilla de verificación que alterna entre dos etiquetas:
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();
});
El código de este ejemplo está disponible en examples/react-testing-library.
Transformadores personalizados
Si necesitas funcionalidad más avanzada, también puedes construir tu propio transformador. En lugar de usar babel-jest, aquí tienes un ejemplo con @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;
},
};
No olvides instalar los paquetes @babel/core y babel-preset-jest para que este ejemplo funcione.
Para que esto funcione con Jest, debes actualizar tu configuración de Jest con esto: "transform": {"\\.js$": "path/to/custom-transformer.js"}.
Si quieres construir un transformador con soporte para Babel, también puedes usar babel-jest para componerlo y pasar tus opciones de configuración personalizadas:
const babelJest = require('babel-jest');
module.exports = babelJest.createTransformer({
presets: ['my-custom-preset'],
});
Consulta la documentación dedicada para más detalles.