使用Jest测试Redux和Axios提取中间件

阿纳托尔·卢塞特(Anatole Lucet)

我有拦截与一个类型包含任何动作中间件:API_REQUEST动作API_REQUEST创建apiRequest()创建了一个动作。当我的中间件拦截一个动作时,它使用Axios发出请求,如果请求成功,则分派由创建的动作apiSuccess()如果throw在请求之后是Axios ,则中间件将调度使用创建的操作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()这只是我从文档的这一部分中获取的功能它允许我嘲笑dispatchgetStatenext

显然这是行不通的,但是我敢肯定有办法。

幻灯片放映

这是单元测试解决方案:

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Jest Express使用参数测试中间件

来自分类Dev

jest.setTimeout.Error:使用Jest和Supertest模拟Express中间件

来自分类Dev

Redux中间件逻辑

来自分类Dev

Redux中间件currying

来自分类Dev

Redux中间件逻辑

来自分类Dev

Redux中间件,笨拙?

来自分类Dev

React确认模式和Redux中间件

来自分类Dev

你如何使用中间件('auth:api')测试路由?

来自分类Dev

Redux Thunk与Redux定制中间件

来自分类Dev

Redux中间件问题-错误:操作必须是纯对象。使用自定义中间件进行异步操作

来自分类Dev

Redux的applyMiddleware无法呈现中间件

来自分类Dev

Redux中间件在reducer之后被调用

来自分类Dev

在 redux 中间件中调用 dispatch

来自分类Dev

Redux 中间件无限循环?

来自分类Dev

Express中间件测试Mocha Chai

来自分类Dev

如何测试路由的中间件?

来自分类Dev

Laravel 测试认证中间件

来自分类Dev

如何测试简单的中间件

来自分类Dev

React和Redux:Redux自定义中间件未执行

来自分类Dev

使用中间件将redux-devtools传递到redux存储

来自分类Dev

如何在 angular-redux 中使用 redux-state-sync 中间件?

来自分类Dev

Express中间件,next和Promises

来自分类Dev

CakePHP 3.6:路由和中间件

来自分类Dev

Redux中间件中的dispatch和next之间有什么区别?

来自分类Dev

使用中间件Laravel或使用If会话?

来自分类Dev

使用的中间件的使用设置

来自分类Dev

使用OWIN中间件记录异常

来自分类Dev

在Koa中使用Express中间件

来自分类Dev

使用Route :: resource的“ auth”中间件