Vai al contenuto principale
{ "message": "Versione: 29.7", "description": "" }

Test di Applicazioni React

Traduzione Beta Non Ufficiale

Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →

In Facebook utilizziamo Jest per testare applicazioni React.

Configurazione

Configurazione con Create React App

Se sei nuovo a React, ti consigliamo di usare Create React App. È pronto all'uso e include Jest! Dovrai solo aggiungere react-test-renderer per il rendering degli snapshot.

Esegui

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

Configurazione senza Create React App

Se hai un'applicazione esistente, dovrai installare alcuni pacchetti per far funzionare tutto correttamente. Utilizziamo il pacchetto babel-jest e il preset Babel react per trasformare il codice nell'ambiente di test. Vedi anche utilizzo di babel.

Esegui

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

Il tuo package.json dovrebbe apparire così (dove <current-version> è l'effettivo numero di versione più recente del pacchetto). Aggiungi gli script e le voci di configurazione di 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 sei pronto a partire!

Snapshot Testing

Creiamo un test di snapshot per un componente Link che renderizza collegamenti ipertestuali:

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

Gli esempi utilizzano componenti a funzione, ma i componenti a classe possono essere testati allo stesso modo. Vedi React: Function and Class Components. Ricorda che con i componenti a classe, ci aspettiamo che Jest venga utilizzato per testare le props e non i metodi direttamente.

Ora utilizziamo il renderer di test di React e la funzionalità snapshot di Jest per interagire con il componente, catturarne l'output renderizzato e creare un file 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 esegui yarn test o jest, verrà prodotto un file di output simile a questo:

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

Al successivo esecuzione dei test, l'output renderizzato verrà confrontato con lo snapshot creato in precedenza. Lo snapshot dovrebbe essere commitato insieme alle modifiche al codice. Quando un test degli snapshot fallisce, devi verificare se si tratta di un cambiamento intenzionale o accidentale. Se il cambiamento è previsto, puoi invocare Jest con jest -u per sovrascrivere lo snapshot esistente.

Il codice per questo esempio è disponibile su examples/snapshot.

Snapshot Testing con Mock, Enzyme e React 16+

C'è un caveat negli snapshot test quando si usano Enzyme e React 16+. Se esegui il mock di un modulo in questo modo:

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

Allora vedrai degli avvisi nella 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 attiva questi avvisi a causa di come verifica i tipi di elemento, e il modulo mockato fallisce questi controlli. Le tue opzioni sono:

  1. Renderizza come testo. In questo modo non vedrai le props passate al componente mockato nello snapshot, ma è semplice:

    jest.mock('./SomeComponent', () => () => 'SomeComponent');
  2. Renderizza come elemento personalizzato. Gli "elementi personalizzati" del DOM non sono verificati e non dovrebbero generare avvisi. Sono in minuscolo e hanno un trattino nel nome.

    jest.mock('./Widget', () => () => <mock-widget />);
  3. Usa react-test-renderer. Questo renderer non si preoccupa dei tipi di elemento e accetterà tranquillamente ad esempio SomeComponent. Puoi verificare gli snapshot con il test renderer e controllare separatamente il comportamento dei componenti con Enzyme.

  4. Disabilita completamente gli avvisi (da fare nel file di setup di Jest):

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

    Normalmente non dovrebbe essere la scelta preferita poiché potresti perdere avvisi utili. Tuttavia, in alcuni casi, ad esempio quando testiamo componenti react-native che renderizzano tag react-native nel DOM, molti avvisi sono irrilevanti. Un'altra opzione è intercettare console.warn e sopprimere avvisi specifici.

DOM Testing

Se vuoi verificare e manipolare i componenti renderizzati, puoi usare @testing-library/react, Enzyme o le TestUtils di React. Il seguente esempio utilizza @testing-library/react.

@testing-library/react

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

Implementiamo una checkbox che alterna due etichette:

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

Il codice per questo esempio è disponibile su examples/react-testing-library.

Trasformatori personalizzati

Se hai bisogno di funzionalità più avanzate, puoi anche creare il tuo trasformatore. Invece di usare babel-jest, ecco un esempio che utilizza @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;
},
};

Non dimenticare di installare i pacchetti @babel/core e babel-preset-jest per far funzionare questo esempio.

Per farlo funzionare con Jest devi aggiornare la tua configurazione Jest con: "transform": {"\\.js$": "path/to/custom-transformer.js"}.

Se vuoi creare un trasformatore con supporto Babel, puoi anche usare babel-jest per comporlo e passare le tue opzioni di configurazione personalizzate:

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

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

Consulta la documentazione dedicata per maggiori dettagli.