Ir para o conteúdo principal
Versão: 29.7

Testando Aplicativos React

Tradução Beta Não Oficial

Esta página foi traduzida por PageTurner AI (beta). Não é oficialmente endossada pelo projeto. Encontrou um erro? Reportar problema →

No Facebook, usamos o Jest para testar aplicativos React.

Configuração

Configuração com Create React App

Se você é novo em React, recomendamos usar o Create React App. Ele já vem pronto para uso e inclui o Jest! Você só precisará adicionar o react-test-renderer para renderizar snapshots.

Execute

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

Configuração sem Create React App

Se você já tem um aplicativo existente, precisará instalar alguns pacotes para que tudo funcione corretamente. Usamos o pacote babel-jest e o preset do Babel react para transformar nosso código no ambiente de teste. Veja também usando babel.

Execute

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

Seu package.json deve ficar mais ou menos assim (onde <current-version> é a versão mais recente do pacote). Adicione os scripts e as entradas de configuração do 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'}],
],
};

E está tudo pronto!

Teste de Snapshot

Vamos criar um teste de snapshot para um componente Link que renderiza hyperlinks:

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>
);
}
"message": "nota"

Os exemplos usam componentes de função, mas componentes de classe podem ser testados da mesma forma. Veja React: Componentes de Função e Classe. Lembrete: com componentes de classe, o Jest deve ser usado para testar props e não métodos diretamente.

Agora vamos usar o test renderer do React e o recurso de snapshot do Jest para interagir com o componente, capturar a saída renderizada e criar um arquivo de snapshot:

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();
});

Quando você executar yarn test ou jest, será gerado um arquivo de saída como este:

__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>
`;

Na próxima execução dos testes, a saída renderizada será comparada com o snapshot criado anteriormente. O snapshot deve ser commitado junto com as alterações de código. Quando um teste de snapshot falhar, você precisa inspecionar se a mudança foi intencional ou não. Se a mudança for esperada, execute o Jest com jest -u para sobrescrever o snapshot existente.

O código deste exemplo está disponível em examples/snapshot.

Teste de Snapshot com Mocks, Enzyme e React 16+

Há uma ressalva sobre teste de snapshot ao usar Enzyme e React 16+. Se você simular um módulo com este estilo:

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

Você verá avisos no 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.

O React 16 aciona esses avisos devido à sua verificação de tipos de elementos, e o módulo simulado falha nessas verificações. Suas opções são:

  1. Renderizar como texto. Você não verá as props passadas para o componente simulado no snapshot, mas é simples:

    jest.mock('./SomeComponent', () => () => 'SomeComponent');
  2. Renderizar como elemento personalizado. Elementos personalizados do DOM não são verificados e não disparam avisos. Eles são minúsculos e têm hífen no nome.

    jest.mock('./Widget', () => () => <mock-widget />);
  3. Usar react-test-renderer. O test renderer não verifica tipos de elementos e aceita tranquilamente SomeComponent. Você pode verificar snapshots com o test renderer e testar comportamento separadamente com Enzyme.

  4. Desativar todos os avisos (deve ser feito no arquivo de setup do Jest):

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

    Essa não deve ser sua opção padrão, pois avisos úteis podem ser perdidos. Porém, em casos como testes de componentes React Native renderizados no DOM, muitos avisos são irrelevantes. Outra opção é interceptar console.warn para suprimir avisos específicos.

Teste de DOM

Para fazer asserções e manipular componentes renderizados, você pode usar @testing-library/react, Enzyme ou TestUtils do React. O exemplo a seguir usa @testing-library/react.

@testing-library/react

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

Vamos implementar um checkbox que alterna entre dois rótulos:

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();
});

O código deste exemplo está disponível em examples/react-testing-library.

Transformadores personalizados

Se você precisa de funcionalidades mais avançadas, também pode construir seu próprio transformador. Em vez de usar babel-jest, veja um exemplo usando @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ão esqueça de instalar os pacotes @babel/core e babel-preset-jest para este exemplo funcionar.

Para fazer isso funcionar com o Jest, você precisa atualizar sua configuração do Jest com: "transform": {"\\.js$": "path/to/custom-transformer.js"}.

Se você quiser construir um transformador com suporte a Babel, também pode usar o babel-jest para compor um e passar suas opções de configuração personalizadas:

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

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

Consulte a documentação dedicada para mais detalhes.