Testing React Apps
Ми використовуємо Jest в Facebook для тестування React додатків.
Налаштування #
Налаштування з Create React App #
Якщо ви тільки починаєте знайомитися з React, ми рекомендуємо вам використовувати Create React App. Це готовий до використання інструмент і він поставляється з Jest! Вам не потрібно робити жодних додаткових налаштувань, тому можете переходити зразу до наступного розділу.
Налаштування без Create React App #
Якщо у вас вже є існуючий додаток, вам потрібно встановити кілька пакунків, щоб змусити все працювати. Ми використовуємо пакет babel-jest
і babel-preset-react
для трансформації нашого коду в тестовому середовищі. Також перегляньте використання babel.
Запустіть
npm install --save-dev jest babel-jest babel-preset-es2015 babel-preset-react react-test-renderer
Ваш package.json
повинен виглядати приблизно так (де <current-version>
- це останні актуальні версії пакетів). Додайте необхідні скрипти і конфігурацію Jest:
// package.json "dependencies": { "react": "<current-version>", "react-dom": "<current-version>" }, "devDependencies": { "babel-jest": "<current-version>", "babel-preset-es2015": "<current-version>", "babel-preset-react": "<current-version>", "jest": "<current-version>", "react-test-renderer": "<current-version>" }, "scripts": { "test": "jest" }
// .babelrc { "presets": ["es2015", "react"] }
І все готово!
Тестування з допомогою знімків #
Давайте створимо тест, що використовує знімок для компонента Link, який відображає гіперпосилання:
// Link.react.js import React from 'react'; const STATUS = { HOVERED: 'hovered', NORMAL: 'normal', }; export default class Link extends React.Component { constructor(props) { super(props); this._onMouseEnter = this._onMouseEnter.bind(this); this._onMouseLeave = this._onMouseLeave.bind(this); this.state = { class: STATUS.NORMAL, }; } _onMouseEnter() { this.setState({class: STATUS.HOVERED}); } _onMouseLeave() { this.setState({class: STATUS.NORMAL}); } render() { return ( <a className={this.state.class} href={this.props.page || '#'} onMouseEnter={this._onMouseEnter} onMouseLeave={this._onMouseLeave}> {this.props.children} </a> ); } }
Тепер давайте використаємо тестовий рендерер React і функцію створення знімків Jest для взаємодії з компонентом, отримання результату його відображення і створення файла знімку:
// Link.react-test.js import React from 'react'; import Link from '../Link.react'; import renderer from 'react-test-renderer'; test('Link 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 tree.props.onMouseEnter(); // re-rendering tree = component.toJSON(); expect(tree).toMatchSnapshot(); // manually trigger the callback tree.props.onMouseLeave(); // re-rendering tree = component.toJSON(); expect(tree).toMatchSnapshot(); });
Коли ви запустите команду npm test
або jest
, то повинні отримати схожий результат:
// __tests__/__snapshots__/Link.react-test.js.snap exports[`Link changes the class when hovered 1`] = ` <a className="normal" href="http://www.facebook.com" onMouseEnter={[Function]} onMouseLeave={[Function]}> Facebook </a> `; exports[`Link changes the class when hovered 2`] = ` <a className="hovered" href="http://www.facebook.com" onMouseEnter={[Function]} onMouseLeave={[Function]}> Facebook </a> `; exports[`Link changes the class when hovered 3`] = ` <a className="normal" href="http://www.facebook.com" onMouseEnter={[Function]} onMouseLeave={[Function]}> Facebook </a> `;
Наступного разу, під час запуску тестів, результат роботи компонента буде порівняний зі збереженим знімком. Знімок варто додавати в систему контроля версій разом зі змінами коду. Коли тест, що використовує знімки, провалиться, вам потрібно буде перевірити чи відбулися навмисні, чи ненавмисні зміни. Якщо зміни були очікувані, ви можете запустити Jest командою jest -u
, щоб оновити існуючі знімки.
Код цього прикладу доступний в examples/snapshot.
DOM тестування #
Якщо ви хочете перевіряти і маніпулювати результатом роботи ваших компонентів, ви можете використати Enzyme чи ReactTestUtils. Ми будемо використовувати Enzyme для цього прикладу.
Щоб використовувати Enzyme, вам потрібно виконати npm install --save-dev enzyme
. Якщо ви використовуєте React з версією до 15.5.0, вам також знадобиться встановити react-addons-test-utils
.
Давайте створимо простий чекбокс, який перемикається між двома підписами:
// CheckboxWithLabel.js import React from 'react'; export default class CheckboxWithLabel extends React.Component { constructor(props) { super(props); this.state = {isChecked: false}; // bind manually because React class components don't auto-bind // http://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding this.onChange = this.onChange.bind(this); } onChange() { this.setState({isChecked: !this.state.isChecked}); } render() { return ( <label> <input type="checkbox" checked={this.state.isChecked} onChange={this.onChange} /> {this.state.isChecked ? this.props.labelOn : this.props.labelOff} </label> ); } }
Використаємо поверхневий рендер Enzyme в цьому прикладі.
// __tests__/CheckboxWithLabel-test.js import React from 'react'; import {shallow} from 'enzyme'; import CheckboxWithLabel from '../CheckboxWithLabel'; test('CheckboxWithLabel changes the text after click', () => { // Render a checkbox with label in the document const checkbox = shallow( <CheckboxWithLabel labelOn="On" labelOff="Off" /> ); expect(checkbox.text()).toEqual('Off'); checkbox.find('input').simulate('change'); expect(checkbox.text()).toEqual('On'); });
Код цього прикладу доступний в examples/enzyme.
Власні перетворювачі коду #
Якщо вам потрібна більш складна функціональність, ви можете створити свій власний перетворювач коду. Ось приклад використання babel, замість babel-jest:
// custom-transformer.js 'use strict'; const babel = require('babel-core'); const jestPreset = require('babel-preset-jest'); module.exports = { process(src, filename) { if (babel.util.canCompile(filename)) { return babel.transform(src, { filename, presets: [jestPreset], retainLines: true, }).code; } return src; }, };
Не забудьте встановити babel-core
і babel-preset-jest
пакети для того, щоб приклад запрацював.
Щоб це запрацювало з Jest вам необхідно оновити вашу Jest конфігурацію цим: "transform": {"\\.js$": "path/to/custom-transformer.js"}
.
Якщо ви хочете створити перетворювач з підтримкою babel, ви також можете використати babel-jest, щоб скомпонувати його і передати в конфігураційні параметри:
const babelJest = require('babel-jest'); module.exports = babelJest.createTransformer({ presets: ['my-custom-preset'], });