doc*_*ock 13 javascript unit-testing node.js jestjs axios
client/index.js我有一个使用 axios 发出请求的函数
import axios from "axios";
const createRequest = async (url, method) => {
const response = await axios({
url: url,
method: method
});
return response;
};
export default { createRequest };
Run Code Online (Sandbox Code Playgroud)
我想使用 测试这个函数jest,所以我创建了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();
})
})
Run Code Online (Sandbox Code Playgroud)
但是当我尝试运行它时,测试失败并且出现此错误
connect ECONNREFUSED 127.0.0.1:80
Run Code Online (Sandbox Code Playgroud)
如果我使用模拟而不是 doMock,那么我会收到此错误 -
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
Run Code Online (Sandbox Code Playgroud)
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" }
}
Run Code Online (Sandbox Code Playgroud)
我在节点环境中运行它,节点版本是14.16.0,笑话版本是26.6.3。请帮助确定这种方法有什么问题以及如何修复它。
jon*_*rpe 16
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).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 });
});
});
Run Code Online (Sandbox Code Playgroud)
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 });
Run Code Online (Sandbox Code Playgroud)
at which point your consumers might as well just be using Axios directly.
jest.doMock(moduleName, factory, options)方法不会自动提升到代码块的顶部。这意味着axios该函数中使用的函数createRequest仍将是原始函数。
你需要使用jest.mock().
例如
\nindex.js:
import axios from \'axios\';\n\nconst createRequest = async (url, method) => {\n const response = await axios({\n url: url,\n method: method,\n });\n return response;\n};\n\nexport default { createRequest };\nRun Code Online (Sandbox Code Playgroud)\nindex.test.js:
import axios from \'axios\';\nimport client from \'./\';\n\njest.mock(\'axios\', () => jest.fn(() => Promise.resolve(\'teresa teng\')));\n\ndescribe(\'Client\', () => {\n it(\'should call axios and return a response\', async () => {\n const response = await client.createRequest(\'http://localhost/\', \'GET\');\n expect(axios).toHaveBeenCalled();\n expect(response).toEqual(\'teresa teng\');\n });\n});\nRun Code Online (Sandbox Code Playgroud)\n单元测试结果:
\n PASS examples/67101502/index.test.js (11.503 s)\n Client\n \xe2\x9c\x93 should call axios and return a response (4 ms)\n\n----------|---------|----------|---------|---------|-------------------\nFile | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s \n----------|---------|----------|---------|---------|-------------------\nAll files | 100 | 100 | 100 | 100 | \n index.js | 100 | 100 | 100 | 100 | \n----------|---------|----------|---------|---------|-------------------\nTest Suites: 1 passed, 1 total\nTests: 1 passed, 1 total\nSnapshots: 0 total\nTime: 13.62 s\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
39607 次 |
| 最近记录: |