Использование react-redux-mock-store

Для эффективного тестирования компонентов React, которые используют Redux, необходимо имитировать состояние хранилища и действия Redux. Модуль react-redux-mock-store предоставляет простое средство для создания поддельных хранилищ Redux, которые могут быть использованы в тестах с Enzyme. Это позволяет точно воспроизвести поведение компонента, не требуя наличия реального Redux-хранилища и асинхронных операций.

Установка react-redux-mock-store

Для начала необходимо установить саму библиотеку, а также её зависимости. Для этого можно воспользоваться пакетным менеджером 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, который передает этот мок-стор. Тест проверяет, что компонент правильно отрендерен с заданным состоянием.

Поддержка Middleware

Одним из преимуществ использования 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 были получены правильные действия.

Использование store.getActions()

Метод 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, в изолированном и контролируемом окружении. Это упрощает процесс тестирования и позволяет точно моделировать поведение компонента при различных состояниях хранилища и при различных действиях.