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.