我有拦截与一个类型包含任何动作中间件: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()
这只是我从文档的这一部分中获取的功能。它允许我嘲笑dispatch
,getState
和next
。
显然这是行不通的,但是我敢肯定有办法。
这是单元测试解决方案:
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] 删除。
我来说两句