Для эффективного тестирования компонентов React, которые используют
Redux, необходимо имитировать состояние хранилища и действия Redux.
Модуль react-redux-mock-store предоставляет простое
средство для создания поддельных хранилищ Redux, которые могут быть
использованы в тестах с Enzyme. Это позволяет точно воспроизвести
поведение компонента, не требуя наличия реального Redux-хранилища и
асинхронных операций.
Для начала необходимо установить саму библиотеку, а также её зависимости. Для этого можно воспользоваться пакетным менеджером npm:
npm install --save-dev redux-mock-store
После этого можно будет импортировать её в тесты и использовать для создания моков хранилища.
redux-mock-store позволяет создать мок-стор с
использованием редьюсеров, middleware и других особенностей Redux.
Пример простого теста, в котором используется мок-стор, может выглядеть
следующим образом:
import React from 'react';
import { shallow } from 'enzyme';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import MyComponent from './MyComponent';
const mockStore = configureMockStore();
const store = mockStore({ someState: 'test' });
describe('MyComponent', () => {
it('renders with mock store', () => {
const wrapper = shallow(
<Provider store={store}>
<MyComponent />
</Provider>
);
expect(wrapper).toMatchSnapshot();
});
});
В этом примере создается мок-стор с начальными значениями состояния,
а затем компонент MyComponent оборачивается в
Provider, который передает этот мок-стор. Тест проверяет,
что компонент правильно отрендерен с заданным состоянием.
Одним из преимуществ использования
react-redux-mock-store является возможность добавления
middleware, которые могут быть полезны в сложных тестах. Например, если
компонент использует асинхронные действия с redux-thunk,
можно добавить middleware для поддержки асинхронности в тестах.
Пример использования middleware с мок-стором:
import React from 'react';
import { shallow } from 'enzyme';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import MyComponent from './MyComponent';
const mockStore = configureMockStore([thunk]);
const store = mockStore({ someState: 'test' });
describe('MyComponent with async action', () => {
it('dispatches async actions', () => {
const wrapper = shallow(
<Provider store={store}>
<MyComponent />
</Provider>
);
store.dispatch({ type: 'ASYNC_ACTION' });
const actions = store.getActions();
expect(actions).toEqual([{ type: 'ASYNC_ACTION' }]);
});
});
В данном примере используется middleware redux-thunk,
который необходим для тестирования асинхронных действий. После диспатча
асинхронного действия, тест проверяет, что в store были
получены правильные действия.
Метод getActions() предоставляет возможность получить
все действия, которые были диспатчены в ходе теста. Это полезно для
проверки того, какие действия были вызваны при взаимодействии с
компонентом.
Пример использования:
import React from 'react';
import { shallow } from 'enzyme';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import MyComponent from './MyComponent';
const mockStore = configureMockStore();
const store = mockStore({});
describe('MyComponent dispatches actions', () => {
it('dispatches the correct actions on button click', () => {
const wrapper = shallow(
<Provider store={store}>
<MyComponent />
</Provider>
);
wrapper.find('button').simulate('click');
const actions = store.getActions();
expect(actions).toEqual([{ type: 'BUTTON_CLICKED' }]);
});
});
Здесь компонент MyComponent диспатчит действие
BUTTON_CLICKED при клике на кнопку. Тест проверяет, что в
результате клика было вызвано правильное действие.
Для тестирования компонентов, которые используют асинхронные
действия, необходимо удостовериться, что мок-стор поддерживает
асинхронные вызовы. В этом случае можно использовать методы
store.dispatch() и getActions() для проверки
того, что были диспатчены ожидаемые действия.
Пример теста с асинхронным действием:
import React from 'react';
import { shallow } from 'enzyme';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import MyComponent from './MyComponent';
const mockStore = configureMockStore([thunk]);
const store = mockStore({ someState: 'test' });
describe('MyComponent async action test', () => {
it('dispatches async action on load', () => {
const wrapper = shallow(
<Provider store={store}>
<MyComponent />
</Provider>
);
store.dispatch((dispatch) => {
setTimeout(() => {
dispatch({ type: 'ASYNC_ACTION_COMPLETE' });
}, 100);
});
return new Promise((resolve) => {
setTimeout(() => {
const actions = store.getActions();
expect(actions).toEqual([{ type: 'ASYNC_ACTION_COMPLETE' }]);
resolve();
}, 200);
});
});
});
В этом примере асинхронное действие выполняется с использованием
setTimeout, и тест проверяет, что действие было правильно
диспатчено по завершению асинхронной операции.
Если в компоненте используется селектор для извлечения данных из состояния Redux, можно протестировать, как компонент реагирует на изменения состояния. В этом случае полезно передать начальное состояние и убедиться, что компонент правильно извлекает данные.
Пример:
import React from 'react';
import { shallow } from 'enzyme';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import { MyComponent } from './MyComponent';
import { getData } from './selectors';
const mockStore = configureMockStore();
const store = mockStore({
someState: { data: 'test' }
});
describe('MyComponent with selector', () => {
it('renders data from selector', () => {
const wrapper = shallow(
<Provider store={store}>
<MyComponent />
</Provider>
);
const data = getData(store.getState());
expect(wrapper.find('.data').text()).toEqual(data);
});
});
В данном примере используется селектор getData, который
извлекает данные из состояния хранилища. Тест проверяет, что компонент
правильно отображает данные, полученные из селектора.
Использование redux-mock-store с Enzyme дает возможность
тестировать компоненты React, которые работают с Redux, в изолированном
и контролируемом окружении. Это упрощает процесс тестирования и
позволяет точно моделировать поведение компонента при различных
состояниях хранилища и при различных действиях.