redux-thunk结构和测试副作用



我正在使用redux-thunk,不确定副作用(showAlertError函数)是否正确地结构。尽管乍看之下我的笑话测试设置似乎还不错,但我会遇到一个错误:

jest.fn()值必须是模拟功能或间谍。收到:未定义的

showAlertError功能在正确的位置,还是应该在动作创建者或其他地方?另外,如果这是正确的位置,那么我如何测试是否称为。

export const submitTeammateInvitation = (data) => {
  const config = {
   // config code
  };
  return async (dispatch) => {
    dispatch(submitTeammateInvitationRequest(data));
    try {
      const response = await fetch(inviteTeammateEndpoint, config);
      const jsonResponse = await response.json();
      if (!response.ok) {
        showErrorAlert(jsonResponse);
        dispatch(submitTeammateInvitationError(jsonResponse));
        throw new Error(response.statusText);
      }
      dispatch(submitTeammateInvitationSuccess(jsonResponse));
    } catch (error) {
      if (process.env.NODE_ENV === 'development') {
        console.log('Request failed', error);
      }
    }
  };
};

测试

import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { showAlertError } from '../../../../_helpers/alerts';
jest.mock('../../../../_helpers/alerts');
const middlewares = [thunk];
const createMockStore = configureMockStore(middlewares);
describe('submitTeammateInvitation', () => {
   it('dispatches the correct actions on a failed fetch request', () => {
     fetch.mockResponse(
      JSON.stringify(error),
      { status: 500, statusText: 'Internal Server Error' }
    );
    const store = createMockStore({});
    const expectedActions = [
      submitTeammateInvitationRequestObject,
      submitTeammateInvitationErrorObject
    ];
    const showAlertError = jest.fn();
    return store.dispatch(submitTeammateInvitation(inviteTeammateEndpoint))
      .then(() => {
        expect(showAlertError).toBeCalled(); // this doesn't work
        expect(store.getActions()).toEqual(expectedActions); // this works
      });
  });
});

您可以手动模拟showErrorAlert函数。这是解决方案:

actionCreators.ts

import fetch from 'node-fetch';
import { showErrorAlert } from './showErrorAlert';
const SUBMIT_TEAMATE_INVITATION_REQUEST = 'SUBMIT_TEAMATE_INVITATION_REQUEST';
const SUBMIT_TEAMATE_INVITATION_SUCCESS = 'SUBMIT_TEAMATE_INVITATION_SUCCESS';
const SUBMIT_TEAMATE_INVITATION_ERROR = 'SUBMIT_TEAMATE_INVITATION_ERROR';
export const submitTeammateInvitationRequest = data => ({ type: SUBMIT_TEAMATE_INVITATION_REQUEST, payload: { data } });
export const submitTeammateInvitationSuccess = data => ({ type: SUBMIT_TEAMATE_INVITATION_SUCCESS, payload: { data } });
export const submitTeammateInvitationError = data => ({ type: SUBMIT_TEAMATE_INVITATION_ERROR, payload: { data } });
export const submitTeammateInvitation = data => {
  const config = {
    // config code
  };
  const inviteTeammateEndpoint = 'https://github.com/mrdulin';
  return async dispatch => {
    dispatch(submitTeammateInvitationRequest(data));
    try {
      const response = await fetch(inviteTeammateEndpoint, config);
      const jsonResponse = await response.json();
      if (!response.ok) {
        showErrorAlert(jsonResponse);
        dispatch(submitTeammateInvitationError(jsonResponse));
        throw new Error(response.statusText);
      }
      dispatch(submitTeammateInvitationSuccess(jsonResponse));
    } catch (error) {
      if (process.env.NODE_ENV === 'development') {
        console.log('Request failed', error);
      }
    }
  };
};

showErrorAlert.ts

export function showErrorAlert(jsonResponse) {
  console.log(jsonResponse);
}

actionCreators.spec.ts

import {
  submitTeammateInvitation,
  submitTeammateInvitationRequest,
  submitTeammateInvitationSuccess,
  submitTeammateInvitationError
} from './actionCreators';
import createMockStore from 'redux-mock-store';
import thunk, { ThunkDispatch } from 'redux-thunk';
import fetch from 'node-fetch';
import { AnyAction } from 'redux';
import { showErrorAlert } from './showErrorAlert';
const { Response } = jest.requireActual('node-fetch');
jest.mock('node-fetch');
jest.mock('./showErrorAlert.ts', () => {
  return {
    showErrorAlert: jest.fn()
  };
});
const middlewares = [thunk];
const mockStore = createMockStore<any, ThunkDispatch<any, any, AnyAction>>(middlewares);
describe('submitTeammateInvitation', () => {
  it('dispatches the correct actions on a failed fetch request', () => {
    const mockedResponse = { data: 'mocked response' };
    const mockedJSONResponse = JSON.stringify(mockedResponse);
    const mockedData = { data: 'mocked data' };
    (fetch as jest.MockedFunction<typeof fetch>).mockResolvedValueOnce(
      new Response(mockedJSONResponse, { status: 500, statusText: 'Internal Server Error' })
    );
    const intialState = {};
    const store = mockStore(intialState);
    const expectedActions = [
      submitTeammateInvitationRequest(mockedData),
      submitTeammateInvitationError(mockedResponse)
    ];
    return store.dispatch(submitTeammateInvitation(mockedData)).then(() => {
      expect(store.getActions()).toEqual(expectedActions);
      expect(showErrorAlert).toBeCalledWith(mockedResponse);
    });
  });
});

带有覆盖范围报告的单位测试结果:

 PASS  src/stackoverflow/47560126/actionCreators.spec.ts
  submitTeammateInvitation
    ✓ dispatches the correct actions on a failed fetch request (11ms)
-------------------|----------|----------|----------|----------|-------------------|
File               |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-------------------|----------|----------|----------|----------|-------------------|
All files          |    89.29 |       50 |    83.33 |    90.91 |                   |
 actionCreators.ts |    89.29 |       50 |    83.33 |    90.91 |             32,35 |
-------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        5.864s

这是完整的演示:https://github.com/mrdulin/jest-codelab/tree/master/master/src/stackoverflow/47560126

最新更新