如何用Jest模拟Axios

如何用Jest模拟Axios

本文介绍了如何用Jest模拟Axios?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 client/index.js 中有一个函数,该函数使用axios发出请求

I have a function in client/index.js which is using axios to make a request

import axios from "axios";

const createRequest = async (url, method) => {
    const response = await axios({
        url: url,
        method: method
    });
    return response;
};

export default { createRequest };

我想使用 jest 测试此功能,所以我创建了 client/index.test.js

I want to test this function using jest, so I created client/index.test.js

import { jest } from "@jest/globals";
import axios from "axios";

import client from "./";

jest.doMock('axios', () => jest.fn(() => Promise.resolve()));

describe("Client", () => {

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

        expect(axios).toHaveBeenCalled();
    })
})

但是当我尝试运行此程序时,测试失败,并且出现此错误

But when I try to run this, the test is failing and I am getting this error

connect ECONNREFUSED 127.0.0.1:80

如果我使用模拟而不是doMock,那么我会收到此错误-

If I use mock instead of doMock, then I am getting this error -

ReferenceError: /Users/project/src/client/index.test.js: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
    Invalid variable access: jest

package.json -

{
    "name": "project",
    "version": "0.0.1",
    "main": "index.js",
    "author": "author",
    "license": "MIT",
    "private": false,
    "type": "module",
    "scripts": {
        "start": "node --experimental-json-modules --experimental-specifier-resolution=node ./src/index.js",
        "start:dev": "nodemon --experimental-json-modules --experimental-specifier-resolution=node ./src/index.js",
        "test": "node --experimental-vm-modules node_modules/.bin/jest",
        "test:dev": "node --experimental-vm-modules node_modules/.bin/jest --watch",
        "test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage",
        "lint": "eslint --fix .",
        "pretty": "prettier --write ."
    },
    "dependencies": {
        "axios": "^0.21.1",
        "express": "^4.17.1"
    },
    "devDependencies": {
        "babel-eslint": "^10.1.0",
        "eslint": "^7.23.0",
        "jest": "^26.6.3",
        "prettier": "^2.2.1",
        "supertest": "^6.1.3"
    },
    "jest": { "testEnvironment": "node" }
}

我正在节点环境中运行它,节点版本为 14.16.0 ,开玩笑的版本为 26.6.3 .请帮助确定这种方法的错误以及如何解决.

I am running this in node env and node version is 14.16.0, jest version is 26.6.3.Please help to identify what is wrong in this approach and how to fix it.

推荐答案

我建议采用一种完全不同的方法来解决此问题.与其尝试模拟Axios(这是一个您没有拥有的相对复杂的API),而不是使用 msw .这使您可以自由重构实现 ,而无需更改测试,从而使您更有信心它仍在工作.您可以执行以下操作:

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:

  • 因素重复配置为 axios.create({baseURL:" http://localhost" ;, ...});
  • 切换到其他请求库(例如 node-fetch ).

如果Axios API更改,您的测试也会开始失败,告诉您的代码不再起作用.进行两次测试,因为仍然可以实现以前的API,因此您的测试结果会通过但会产生误导.

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.

以下是这种测试的外观;请注意,根本没有提到Axios,现在只是实现细节,我们只关心行为:

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 });
    });
});

请注意,我必须使用 .toMatchObject ,因为您要暴露整个Axios响应对象,该对象包含很多属性.对于您的客户端来说,这不是一个很好的API,因为现在使用该客户端的所有都在使用Axios API.这使您与之紧密相连,并稀释了我上面提到的好处.

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 });

此时,您的消费者也可能直接使用Axios.

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

这篇关于如何用Jest模拟Axios?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-11 08:40