How to mock Axios with Jest?

I would recommend an entirely different way of approaching this. Rather than trying to mock Axios, which is a relatively complicated API that you don’t own, test at the network boundary using a tool like msw. This allows you to freely refactor the implementation without needing to change the tests, giving you more confidence it’s still working. You could do things like:

  • Factor out repeated config to axios.create({ baseURL: "http://localhost", ... });
  • Switch to a different library for the requests (e.g. node-fetch).

Also if the Axios API changed your tests would start failing, telling you your code no longer works. With a test double, as that would still implement the previous API, you’d have passing but misleading test results.

Here’s how that kind of test might look; note that Axios isn’t mentioned at all, it’s just an implementation detail now and we only care about the behaviour:

import { rest } from "msw";
import { setupServer } from "msw/node";

import client from "./";

const body = { hello: "world" };

const server = setupServer(
  rest.get("http://localhost", (_, res, ctx) => {
    return res(ctx.status(200), ctx.json(body))
  })
);

describe("Client", () => {
    beforeAll(() => server.listen());

    afterEach(() => server.resetHandlers());

    afterAll(() => server.close());

    it("should call the API and return a response", async () => {
        const response = await client.createRequest("http://localhost/", "GET");

        expect(response).toMatchObject({ data: body, status: 200 });
    });
});

Note I’ve had to use .toMatchObject because you’re exposing the whole Axios response object, which contains a lot of properties. This isn’t a good API for your client, because now everything using the client is consuming the Axios API; this makes you heavily coupled to it, and dilutes the benefits I mentioned above.

I’m not sure how you’re planning to use it, but I’d be inclined to hide the details of the transport layer entirely – things like status codes, headers etc. are not likely relevant to the business logic in the consumer. Right now you really just have:

const createRequest = (url, method) => axios({ method, url });

at which point your consumers might as well just be using Axios directly.

Leave a Comment