Here is the unit test solution:
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,
});
});
});
});
});
Unit test results with coverage report:
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
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59754838