Использование enzyme для тестирования компонентов в React Native
Начиная с версии v0.18, React Native использует React в качестве зависимости, а не форкнутой версии библиотеки, что означает, что теперь можно использовать shallow enzyme с компонентами React Native.
К сожалению, React Native имеет множество зависимостей от среды, которые трудно смоделировать без устройства-хоста.
Это может быть сложно, когда вы хотите, чтобы ваш набор тестов выполнялся на типичных серверах непрерывной интеграции, таких как Travis.
Для использования enzyme для тестирования React Native в настоящее время необходимо настроить адаптер и загрузить эмулированную DOM.
Настройка адаптера
Хотя адаптер для React Native находится в обсуждении, можно использовать стандартный адаптер, такой как 'enzyme-adapter-react-16':
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });
Загрузка эмулированной DOM с помощью JSDOM
Для использования mount enzyme до появления адаптера для React Native необходимо загрузить эмулированную DOM.
Хотя некоторые добились успеха с react-native-mock-renderer, рекомендуемый подход заключается в использовании https://github.com/tmpvar/jsdom, как документировано на странице документации JSDOM для enzyme здесь.
JSDOM обеспечит все enzyme поведение, которого вы ожидаете. Хотя тестирование снимков Jest также может быть использовано с этим подходом, это не рекомендуется и поддерживается только через wrapper.debug().
Использование enzyme's find при отсутствии атрибута className
Стоит отметить, что React Native позволяет использовать атрибут testID, который может использоваться в качестве селектора, аналогичного className в стандартном React:
<View key={key} style={styles.todo} testID="todo-item">
<Text testID="todo-title" style={styles.title}>{todo.title}</Text>
</View>
expect(wrapper.findWhere((node) => node.prop('testID') === 'todo-item')).toExist();
Пример конфигурации по умолчанию для Jest и замены JSDOM
Для выполнения необходимой конфигурации в вашем фреймворке тестирования рекомендуется использовать скрипт настройки, например, с setupFilesAfterEnv настройкой Jest.
Создайте или обновите файл jest.config.js в корне вашего проекта, чтобы включить настройку setupFilesAfterEnv:
// jest.config.js
module.exports = {
// Load setup-tests.js before test execution
setupFilesAfterEnv: '<rootDir>setup-tests.js',
// ...
};
Затем создайте или обновите файл, указанный в setupFilesAfterEnv, в данном случае setup-tests.js в корне проекта:
// setup-tests.js
import 'react-native';
import 'jest-enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Enzyme from 'enzyme';
/**
* Set up DOM in node.js environment for Enzyme to mount to
*/
const { JSDOM } = require('jsdom');
const jsdom = new JSDOM('<!doctype html><html><body></body></html>');
const { window } = jsdom;
function copyProps(src, target) {
Object.defineProperties(target, {
...Object.getOwnPropertyDescriptors(src),
...Object.getOwnPropertyDescriptors(target),
});
}
global.window = window;
global.document = window.document;
global.navigator = {
userAgent: 'node.js',
};
copyProps(window, global);
/**
* Set up Enzyme to mount to DOM, simulate events,
* and inspect the DOM in tests.
*/
Enzyme.configure({ adapter: new Adapter() });
Настройка enzyme с другими библиотеками тестов и включение JSDOM на лету
Обновите файл, указанный в setupFilesAfterEnv, в данном случае setup-tests.js в корне проекта:
import 'react-native';
import 'jest-enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Enzyme from 'enzyme';
/**
* Set up Enzyme to mount to DOM, simulate events,
* and inspect the DOM in tests.
*/
Enzyme.configure({ adapter: new Adapter() });
Создайте отдельный файл тестов
Создайте файл, начинающийся с префикса enzyme.test.ts, например component.enzyme.test.js:
/**
* @jest-environment jsdom
*/
import React from 'react';
import { mount } from 'enzyme';
import { Text } from '../../../component/text';
describe('Component tested with airbnb enzyme', () => {
test('App mount with enzyme', () => {
const wrapper = mount(<Text />);
// other tests operations
});
});
Самое важное — убедиться, что тест выполняется с jestEnvironment, установленным в jsdom. Один из способов — включить комментарий /* @jest-environment jsdom */ в верхней части файла.
После этого вы сможете начать писать тесты!
Обратите внимание, что вам, возможно, потребуется выполнить дополнительное моделирование собственных компонентов или, если вы хотите выполнить тестирование снимков для компонентов React Native. Обратите внимание, что вам может потребоваться смоделировать KeyGenerator React Navigation в этом случае, чтобы избежать случайных ключей React, которые приведут к постоянной неудаче снимков.
import React from 'react';
import renderer from 'react-test-renderer';
import { mount, ReactWrapper } from 'enzyme';
import { Provider } from 'mobx-react';
import { Text } from 'native-base';
import { TodoItem } from './todo-item';
import { TodoList } from './todo-list';
import { todoStore } from '../../stores/todo-store';
// https://github.com/react-navigation/react-navigation/issues/2269
// React Navigation generates random React keys, which makes
// snapshot testing fail. Mock the randomness to keep from failing.
jest.mock('react-navigation/src/routers/KeyGenerator', () => ({
generateKey: jest.fn(() => 123),
}));
describe('todo-list', () => {
describe('enzyme tests', () => {
it('can add a Todo with Enzyme', () => {
const wrapper = mount(
<Provider keyLength={0} todoStore={todoStore}>
<TodoList />
</Provider>,
);
const newTodoText = 'I need to do something...';
const newTodoTextInput = wrapper.find('Input').first();
const addTodoButton = wrapper
.find('Button')
.findWhere((w) => w.text() === 'Add Todo')
.first();
newTodoTextInput.props().onChangeText(newTodoText);
// Enzyme usually allows wrapper.simulate() alternatively, but this doesn't support 'press' events.
addTodoButton.props().onPress();
// Make sure to call update if external events (e.g. Mobx state changes)
// result in updating the component props.
wrapper.update();
// You can either check for a testID prop, similar to className in React:
expect(
wrapper.findWhere((node) => node.prop('testID') === 'todo-item'),
).toExist();
// Or even just find a component itself, if you broke the JSX out into its own component:
expect(wrapper.find(TodoItem)).toExist();
// You can even do snapshot testing,
// if you pull in enzyme-to-json and configure
// it in snapshotSerializers in package.json
expect(wrapper.find(TodoList)).toMatchSnapshot();
});
});
});
© 2015 Airbnb, Inc.
Licensed under the MIT License.
https://enzymejs.github.io/enzyme/docs/guides/react-native.html