как протестировать действие redux, которое отправляет другое действие

#reactjs #redux #react-redux #jestjs #react-testing-library

Вопрос:

Я использую библиотеку тестирования jest react

И, как описано в названии, я пытаюсь проверить, что testAction отправляет второе действие, но я получаю сообщение об ошибке, в котором говорится, что второе действие не было отправлено, что не может быть правдой, потому что массаж функции печатается.

у кого-нибудь есть подсказка, как я могу заставить этот тест работать?

действие

 export const testAction = () => async (dispatch) => {
    dispatch(secondTestAction());
};
export const secondTestAction = () => {
    console.log('second action have been dispatched');
};

 

испытание

 import '@testing-library/jest-dom/extend-expect';
import * as userActions from '../../../redux/actions/userActions';
import store from '../../../redux/store';

const secondTestActionSpy = jest.spyOn(userActions, 'secondTestAction');

test('should dispatch secondTestActions ', async () => {
    await store.dispatch(userActions.testAction());
    expect(secondTestActionSpy.mock.calls.length).toBe(1);
});

 

ошибка

 expect(received).toBe(expected) // Object.is equality
Expected: 1
Received: 0
expect(secondTestActionSpy.mock.calls.length).toBe(1);
 

Ответ №1:

Видите ли, вы импортируете все свои действия(это export ред.) в тест. Затем вы локально — только в тесте — заменяете одно из импортированных действий оболочкой-шпионом. Но действия в userActions.js не попадают в ваш тестовый файл, одна функция просто вызывает другую, напрямую, только ту версию, которая объявлена, а не завернутую со шпионской версией.

Что ты можешь сделать? Если вы хотите следовать подходу «проверьте, что действие было вызвано», вы можете использовать redux-mock-store :

 import { testAction, secondAction } from '../../../redux/actions/userActions';

test('should dispatch secondTestActions ', async () => {
    mockedStore.dispatch(userActions.testAction());
    expect(mockedStore.getActions()).toContainEqual(secondAction());
});

 

так как же это сделать mockedStore ? Просто используйте экспорт по умолчанию из redux-mock-store :

 import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';

const middlewares = [thunk];
const mockStore = configureMockStore(middlewares)(constantStoreData);
 

Поскольку redux-mock-store не обрабатывает действия с помощью редукторов, на самом деле это не требует, чтобы вы полагались на редукторы. Так что ни ../store то, ни другое здесь не нужно.

Но таким образом вы просто проверяете, какие действия называются(и в каком порядке — если вы используете expect(store.getActions()).toEqual(... вместо expect(store.getActions()).toContainEqual(... ).

На мой взгляд, гораздо проще и надежнее тестировать хранилище в целом — как с помощью действий, так и с помощью редукторов, издеваясь только над взаимодействием с внешним миром(например, запросы к серверу, операции с локальным хранилищем и т. Д.). Затем тесты отправят некоторые действия и обеспечат, чтобы конечное состояние соответствовало ожиданиям:

 it('sets loading once request started', () => {
  const realStore = createStore(userReducer, someInitialState);
  realStore.dispatch(loadUsersAction(someArgument));
  expect(realStore.getState().isLoading).toEqual(true);
});

it('unsets loading once loading users failed', async () => {
  // mock specific request to fail
  const realStore = createStore(userReducer, someInitialState);
  realStore.dispatch(loadUsersAction(someArgument));
  await undefined; // let's skip Promises queue once; does nothing special, await 42 would work the same;
  expect(realStore.getState().isLoading).toEqual(false);
});