React-Apps testen
Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →
Bei Facebook verwenden wir Jest, um React-Anwendungen zu testen.
Einrichtung
Bei bestehenden Anwendungen müssen Sie einige Pakete installieren, damit alles reibungslos funktioniert. Wir verwenden das babel-jest-Paket und das react-Babel-Preset, um Code in der Testumgebung zu transformieren. Siehe auch Babel-Nutzung.
Ausführen:
- 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
Ihre package.json sollte etwa so aussehen (wobei <current-version> die aktuelle Paketversion ist). Fügen Sie die Skripte und Jest-Konfigurationseinträge hinzu:
{
"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'}],
],
};
Und schon können Sie loslegen!
Snapshot-Tests
Erstellen wir einen Snapshot-Test für eine Link-Komponente, die Hyperlinks rendert:
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>
);
}
Beispiele verwenden Funktionskomponenten, aber Klassenkomponenten können analog getestet werden. Siehe React: Funktions- und Klassenkomponenten. Hinweis: Bei Klassenkomponenten soll Jest Props testen, nicht direkt Methoden.
Verwenden wir nun den Test-Renderer von React und die Snapshot-Funktion von Jest, um mit der Komponente zu interagieren, die gerenderte Ausgabe zu erfassen und eine Snapshot-Datei zu erstellen:
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();
});
Wenn Sie yarn test oder jest ausführen, wird eine Ausgabedatei wie die folgende erzeugt:
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>
`;
Beim nächsten Testlauf wird die gerenderte Ausgabe mit der zuvor erstellten Snapshot verglichen. Die Snapshot sollte zusammen mit Codeänderungen committet werden. Wenn ein Snapshot-Test fehlschlägt, müssen Sie prüfen, ob es sich um eine beabsichtigte oder unbeabsichtigte Änderung handelt. Wenn die Änderung erwartet wurde, können Sie Jest mit jest -u aufrufen, um den bestehenden Snapshot zu überschreiben.
Der Beispielcode ist unter examples/snapshot verfügbar.
Snapshot-Tests mit Mocks, Enzyme und React 16+
Bei Snapshot-Tests mit Enzyme und React 16+ gibt es eine Einschränkung. Wenn Sie ein Modul so mocken:
jest.mock('../SomeDirectory/SomeComponent', () => 'SomeComponent');
Erhalten Sie Konsolenwarnungen:
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 löst diese Warnungen durch seine Elementtyp-Prüfung aus, die das gemockte Modul nicht besteht. Ihre Optionen:
-
Als Text rendern: Props werden im Snapshot nicht sichtbar, aber es ist einfach:
jest.mock('./SomeComponent', () => () => 'SomeComponent'); -
Als benutzerdefiniertes Element rendern: DOM-"Custom Elements" werden nicht geprüft und lösen keine Warnungen aus. Sie sind kleingeschrieben und enthalten Bindestriche:
jest.mock('./Widget', () => () => <mock-widget />); -
react-test-renderernutzen: Dieser kümmert sich nicht um Elementtypen und akzeptiert z.B.SomeComponent. Prüfen Sie Snapshots mit dem Test-Renderer und Komponentenverhalten separat mit Enzyme. -
Warnungen global deaktivieren (in Ihrer Jest-Setup-Datei):
jest.mock('fbjs/lib/warning', () => require('fbjs/lib/emptyFunction'));Dies ist normalerweise nicht empfehlenswert, da nützliche Warnungen verloren gehen. Bei React-Native-Tests sind jedoch viele DOM-Warnungen irrelevant. Alternativ können Sie console.warn anpassen und spezifische Warnungen unterdrücken.
DOM-Tests
Zum Überprüfen und Manipulieren gerenderter Komponenten können Sie @testing-library/react, Enzyme oder Reacts TestUtils nutzen. Das folgende Beispiel verwendet @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
Implementieren wir eine Checkbox, die zwischen zwei Beschriftungen wechselt:
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();
});
Den Code für dieses Beispiel finden Sie unter examples/react-testing-library.
Benutzerdefinierte Transformer
Wenn Sie erweiterte Funktionen benötigen, können Sie auch Ihren eigenen Transformer erstellen. Hier ein Beispiel mit @babel/core anstelle von babel-jest:
'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;
},
};
Vergessen Sie nicht, die Pakete @babel/core und babel-preset-jest zu installieren, damit dieses Beispiel funktioniert.
Für die Integration mit Jest aktualisieren Sie Ihre Jest-Konfiguration mit: "transform": {"\\.js$": "path/to/custom-transformer.js"}.
Falls Sie einen Transformer mit Babel-Unterstützung erstellen möchten, können Sie babel-jest verwenden, um einen zu komponieren und Ihre benutzerdefinierten Konfigurationsoptionen zu übergeben:
const babelJest = require('babel-jest');
module.exports = babelJest.createTransformer({
presets: ['my-custom-preset'],
});
Weitere Details finden Sie in der dedizierten Dokumentation.