How do I test axios in Jest?

Viewed 211952

I have this action in React:

export function fetchPosts() {
    const request = axios.get(`${WORDPRESS_URL}`);
    return {
        type: FETCH_POSTS,
        payload: request
    }
}

How do I test Axios in this case?

Jest has this use case on their site for asynchronous code where they use a mock function, but can I do this with Axios?

Reference: An Async Example

I have done this so far to test that it is returning the correct type:

it('should dispatch actions with the correct type', () => {
    store.dispatch(fetchPosts());
    let action = store.getActions();
    expect(action[0].type).toBe(FETCH_POSTS);
});

How can I pass in mock data and test that it returns?

7 Answers

Without using any other libraries:

import * as axios from "axios";

// Mock out all top level functions, such as get, put, delete and post:
jest.mock("axios");

// ...

test("good response", () => {
  axios.get.mockImplementation(() => Promise.resolve({ data: {...} }));
  // ...
});

test("bad response", () => {
  axios.get.mockImplementation(() => Promise.reject({ ... }));
  // ...
});

It is possible to specify the response code:

axios.get.mockImplementation(() => Promise.resolve({ status: 200, data: {...} }));

It is possible to change the mock based on the parameters:

axios.get.mockImplementation((url) => {
    if (url === 'www.example.com') {
        return Promise.resolve({ data: {...} });
    } else {
        //...
    }
});

Jest v23 introduced some syntactic sugar for mocking Promises:

axios.get.mockImplementation(() => Promise.resolve({ data: {...} }));

It can be simplified to

axios.get.mockResolvedValue({ data: {...} });

There is also an equivalent for rejected promises: mockRejectedValue.

Further Reading:

I could do that following the steps:

  1. Create a folder __mocks__/ (as pointed by @Januartha comment)
  2. Implement an axios.js mock file
  3. Use my implemented module on test

The mock will happen automatically

Example of the mock module:

module.exports = {
    get: jest.fn((url) => {
        if (url === '/something') {
            return Promise.resolve({
                data: 'data'
            });
        }
    }),
    post: jest.fn((url) => {
        if (url === '/something') {
            return Promise.resolve({
                data: 'data'
            });
        }
        if (url === '/something2') {
            return Promise.resolve({
                data: 'data2'
            });
        }
    }),
    create: jest.fn(function () {
        return this;
    })
};

Look at this

  1. The function to test album.js
const fetchAlbum = function () {
 return axios
   .get("https://jsonplaceholder.typicode.com/albums/2")
   .then((response) => {
     return response.data;
   });
};
  1. The test album.test.js
const axios = require("axios");
const { fetchAlbum } = require("../utils.js");

jest.mock("axios");

test("mock axios get function", async () => {
    expect.assertions(1);
    const album = {
      userId: 1,
      id: 2,
      title: "sunt qui excepturi placeat culpa",
    };
    const payload = { data: album };
    // Now mock axios get method
    axios.get = jest.fn().mockResolvedValue(payload);
    await expect(fetchAlbum()).resolves.toEqual(album);
  });

New tools for testing have been introduced since the question was initially answered.

The problem with mocking is that you often test the mock and not the real context of your code, leaving some areas of this context untested. An improvement over telling axios what promise to return is intercepting http requests via Service Workers.

Service worker is a client-side programmable proxy between your web app and the outside world. So instead of mocking promise resolution it is a more broader solution to mock the proxy server itself, intercepting requests to be tested. Since the interception happens on the network level, your application knows nothing about the mocking.

You can use msw (Mock Service Worker) library to do just that. Here is a short video explaining how it works.

The most basic setup I can think of is this: 1️⃣ set up handlers, which are similar to express.js routing methods; 2️⃣ set up mock server and pass handlers as it’s arguments; 3️⃣ configure tests to so that mock server will intercept our requests; 4️⃣ perform tests; 5️⃣ close mock server.

Say you want to test the following feature:

import axios from "axios";

export const fetchPosts = async () => {
  const request = await axios.get("/some/endpoint/");
  return {
    payload: request,
  };
};

Then test could look like this:

import { rest } from "msw";
import { setupServer } from "msw/node";
import fetchPosts from "./somewhere";

// handlers are usually saved in separate file(s) in one  destined place of the app,
// so that you don't have to search for them when the endpoints have changed
const handlers = [ 1️⃣
  rest.get("/some/endpoint/", (req, res, ctx) =>
    res(ctx.json({ message: "success" }))
  ),
];

const server = setupServer(...handlers); 2️⃣

beforeAll(() => {
  server.listen(); 3️⃣
});

describe("fetchPosts", () => {
  it("should return 'success' message", async () => {
    const resp = await fetchPosts();
    expect(resp.payload?.data?.message).toEqual("success"); 4️⃣
  });
});

afterAll(() => {
  server.close(); 5️⃣
});

The configuration may be different depending on framework you are using. Some general examples for, among others, React (both REST and GraphQL) and Angular can be found on MSW’ repo. A Vue example is provided by VueMastery. You can also find examples on MSW' recipes page.

Related