ค้นหาเว็บไซต์

javascript - การทดสอบ Redux & Axios fetch middleware ด้วย Jest


ฉันมีมิดเดิลแวร์ซึ่งดักจับทุกแอ็กชันที่มีประเภทที่รวม: API_REQUEST แอ็กชันที่มี API_REQUEST ถูกสร้างขึ้นโดยครีเอเตอร์แอ็กชัน apiRequest() เมื่อมิดเดิลแวร์ของฉันดักจับแอ็กชัน มันจะทำการร้องขอโดยใช้ Axios หากการร้องขอสำเร็จ มันจะ dispatch แอ็กชันที่ถูกสร้างโดย apiSuccess() หาก Axios throw หลังจากการร้องขอ มิดเดิลแวร์จะ dispatch แอ็กชันที่สร้างขึ้นด้วย apiError()

มิดเดิลแวร์ :

const apiMiddleware: Middleware = ({ dispatch }) => next => async (action): Promise<void> => {
    next(action);

    if (action.type.includes(API_REQUEST)) {
        const body = action.payload;
        const { url, method, feature } = action.meta;

        try {
            const response = await axios({ method, url, data: body });
            dispatch(apiSuccess({ response, feature }));
        } catch (error) {
            console.error(error);
            dispatch(apiError({ error, feature }));
        }
    }
};

นี่คือวิธีที่มิดเดิลแวร์ API ของฉันทำงาน

ตอนนี้ฉันสงสัยว่าฉันสามารถทดสอบมันโดยใช้ Jest ได้อย่างไร บางทีฉันอาจจะจำลอง Axios เพื่อให้มันสร้างคำร้องขอปลอมในมิดเดิลแวร์ แต่จะทำยังไงดี

นี่คือไฟล์ทดสอบปัจจุบันที่ฉันมี:

describe('api middleware', () => {
    const feature = 'test_feat';

    it('calls next', () => {
        const { invoke, next } = create(apiMiddleware);
        const action = { type: 'TEST' };

        invoke(action);

        expect(next).toHaveBeenCalledWith(action);
    });

    it('dispatch api success on request success', () => {
        const { invoke, next, store } = create(apiMiddleware);
        const action = actions.apiRequest({ body: null, method: 'GET', url: '', feature });
        const data = { test: 'test data' };

        jest.mock('axios');

        invoke(action);

        expect(next).toHaveBeenCalledWith(action);

        expect(store.dispatch).toHaveBeenCalledWith(actions.apiSuccess({
            response: axios.mockResolvedValue({ data }),
            feature,
        }));
    });
});

create() เป็นเพียงฟังก์ชันที่ฉันนำมาจากส่วนนี้ของเอกสาร มันอนุญาตให้ฉันสร้างม็อก dispatch, getState และ next

อย่างชัดเจนว่าสิ่งนี้ใช้ไม่ได้ผล แต่ฉันมั่นใจว่าต้องมีวิธีอยู่

คำตอบ #1

นี่คือตัวอย่างวิธีแก้ปัญหาของการทดสอบหน่วย:

api.middleware.ts:

import { Middleware } from 'redux';
import axios from 'axios';
import { API_REQUEST } from './actionTypes';
import { apiSuccess, apiError } from './actionCreator';

export const apiMiddleware: Middleware = ({ dispatch }) => (next) => async (action): Promise<void> => {
  next(action);

  if (action.type.includes(API_REQUEST)) {
    const body = action.payload;
    const { url, method, feature } = action.meta;

    try {
      const response = await axios({ method, url, data: body });
      dispatch(apiSuccess({ response, feature }));
    } catch (error) {
      console.error(error);
      dispatch(apiError({ error, feature }));
    }
  }
};

actionTypes.ts:

export const API_REQUEST = 'API_REQUEST';
export const API_REQUEST_SUCCESS = 'API_REQUEST_SUCCESS';
export const API_REQUEST_FAILURE = 'API_REQUEST_FAILURE';

actionCreator.ts:

import { API_REQUEST_SUCCESS, API_REQUEST_FAILURE } from './actionTypes';

export function apiSuccess(data) {
  return {
    type: API_REQUEST_SUCCESS,
    ...data,
  };
}
export function apiError(data) {
  return {
    type: API_REQUEST_FAILURE,
    ...data,
  };
}

api.middleware.test.ts:

import { apiMiddleware } from './api.middleware';
import axios from 'axios';
import { MiddlewareAPI } from 'redux';
import { API_REQUEST, API_REQUEST_SUCCESS, API_REQUEST_FAILURE } from './actionTypes';

jest.mock('axios', () => jest.fn());

describe('59754838', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });
  describe('#apiMiddleware', () => {
    describe('Unit test', () => {
      it('should dispatch api success action', async () => {
        const store: MiddlewareAPI = { dispatch: jest.fn(), getState: jest.fn() };
        const next = jest.fn();
        const action = {
          type: API_REQUEST,
          payload: {},
          meta: { url: 'http://localhost', method: 'get', feature: 'feature' },
        };
        const mResponse = { name: 'user name' };
        (axios as jest.Mocked<any>).mockResolvedValueOnce(mResponse);
        await apiMiddleware(store)(next)(action);
        expect(next).toBeCalledWith(action);
        expect(axios).toBeCalledWith({ method: action.meta.method, url: action.meta.url, data: action.payload });
        expect(store.dispatch).toBeCalledWith({
          type: API_REQUEST_SUCCESS,
          response: mResponse,
          feature: action.meta.feature,
        });
      });

      it('should dispatch api error action', async () => {
        const store: MiddlewareAPI = { dispatch: jest.fn(), getState: jest.fn() };
        const next = jest.fn();
        const action = {
          type: API_REQUEST,
          payload: {},
          meta: { url: 'http://localhost', method: 'get', feature: 'feature' },
        };
        const mError = new Error('network error');
        (axios as jest.Mocked<any>).mockRejectedValueOnce(mError);
        await apiMiddleware(store)(next)(action);
        expect(next).toBeCalledWith(action);
        expect(axios).toBeCalledWith({ method: action.meta.method, url: action.meta.url, data: action.payload });
        expect(store.dispatch).toBeCalledWith({
          type: API_REQUEST_FAILURE,
          error: mError,
          feature: action.meta.feature,
        });
      });
    });
  });
});

ผลลัพธ์ของการทดสอบหน่วยพร้อมรายงานความครอบคลุม:

 PASS  src/stackoverflow/59754838/api.middleware.test.ts (11.206s)
  59754838
    #apiMiddleware
      Unit test
        ✓ should dispatch api success action (21ms)
        ✓ should dispatch api error action (23ms)

  console.error src/stackoverflow/59754838/api.middleware.ts:3460
    Error: network error
        at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59754838/api.middleware.test.ts:42:24
        at step (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59754838/api.middleware.test.ts:33:23)
        at Object.next (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59754838/api.middleware.test.ts:14:53)
        at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59754838/api.middleware.test.ts:8:71
        at new Promise (<anonymous>)
        at Object.<anonymous>.__awaiter (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59754838/api.middleware.test.ts:4:12)
        at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59754838/api.middleware.test.ts:34:46)
        at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:102:37)
        at resolve (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
        at new Promise (<anonymous>)
        at mapper (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
        at promise.then (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
        at process._tickCallback (internal/process/next_tick.js:68:7)

-------------------|----------|----------|----------|----------|-------------------|
File               |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-------------------|----------|----------|----------|----------|-------------------|
All files          |      100 |       50 |      100 |      100 |                   |
 actionCreator.ts  |      100 |      100 |      100 |      100 |                   |
 actionTypes.ts    |      100 |      100 |      100 |      100 |                   |
 api.middleware.ts |      100 |       50 |      100 |      100 |                 9 |
-------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        12.901s

ซอร์สโค้ด: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59754838

บทความที่เกี่ยวข้อง