redux-thunk structure and test side effects

馋奶兔 提交于 2019-12-11 09:29:08

问题


I am using redux-thunk and not sure if side effects (showAlertError function) are structured properly. Although my jest test setup seems to be fine at first glance, I get an error:

jest.fn() value must be a mock function or spy. Received: undefined`

Is the showAlertError function is at the right place or it should be in the action creator or somewhere else? Also if this is the right place for it then how I can test if it's called.

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);
      }
    }
  };
};

test

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
      });
  });
});

回答1:


You can mock showErrorAlert function manually. Here is the solution:

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);
    });
  });
});

Unit test result with coverage report:

 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

Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/47560126



来源:https://stackoverflow.com/questions/47560126/redux-thunk-structure-and-test-side-effects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!