Testing Async Middleware

Let’s see how we can test async middleware.

For a more complete example, let’s use the following API middleware:

import axios from 'axios';
import { API } from 'consts';
import { apiStarted, apiFinished, apiError } from 'actions/ui';

const apiMiddleware = ({ dispatch, getState }) => next => action => {
  if (action.type !== API) {
    return next(action);

  const { url, success } = action.payload;
  const headers = {};
  const accessToken = (getState() || {}).accessToken;

  if (accessToken) {
    headers['Access-Token'] = accessToken;


  return axios.request({ url, headers })
    .then(response => {
    .catch(params => dispatch(apiError(new Error(params))));

export default apiMiddleware;

Our middleware catches any actions of type 'API'. The API in question must contain a payload key with a url to request to and a success parameter that holds an action creator to call with the returned data:

const setData = data => ({
  type: 'SET_DATA',
  payload: data

const apiAction = () => ({
  type: API,
  payload: {
    success: setData,
    url: 'fake.json'

In our Redux call, calling dispatch(apiAction()) will result in our API middleware submitting a GET request for server/fake.json and, if successful, dispatching the SET_DATA action with the response set as payload. When there is an error, an action created by apiError() will be dispatched containing status and statusText.

API middleware will dispatch apiStarted() before contacting the server and apiFinished() on success (or apiError() on failure). This allows the application to keep track of the number of active requests to the server and display a spinner or some other user indication.

To fully test this middleware, we can split the tests into three groups: general tests, success tests, and failure tests.

Get hands-on with 1200+ tech skills courses.