node.js - 异步外部函数留下开放句柄 - Jest、Supertest、Express
问题描述
我开始使用 Jest 和 Supertest(用于端点)测试我的应用程序。测试工作顺利,但Jest 在运行测试后检测到 2 个打开的句柄,这会阻止 Jest 干净地退出。
这个打开的句柄是由在我的测试文件中调用的外部异步函数生成的。我正在使用外部函数从 Auth0 API 请求 JWT 令牌;但是对 Auth0 的请求还在其响应中提供了传递端点中间件的关键信息(有关此内容的更多信息,请参见下文)。这里要记住两件事:
- 到目前为止,我无法避免从 Auth0 请求令牌,因为正如我所说,该响应还包含一个
user
带有关键信息的对象。Auth0 将此对象设置在主体响应之外,在同一级别,但不在其中。该信息是传递端点中间件的关键。 - 我已经隔离了所有错误,以确保仅当我调用从 Auth0 API 请求令牌和用户信息的外部异步函数时才会出现问题;
getToken
该问题是通过在测试文件中使用该函数(称为)生成的。
测试文件代码
import app from "../app";
import mongoose from "mongoose";
import supertest from "supertest";
import { getToken } from "../helpers";
import dotenv from "dotenv";
import * as config from "../config";
dotenv.config();
const api = supertest(app);
let authToken: any;
let db: any;
beforeAll(async() => {
try {
mongoose.connect(config.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
});
db = mongoose.connection;
db.on("error", console.error.bind(console, "Console Error:"));
db.once("open", () =>
console.log(`App connected to "${db.name}" database`)
);
authToken = await getToken()
} catch (err) {
return err
}
});
describe("GET /interview/:idCandidate", () => {
test("With auth0 and read permissions", async () => {
await api
.get("/interview/1")
.set("Authorization", "Bearer " + authToken)
.expect(200)
});
});
afterAll(async () => {
try {
await db.close();
} catch (err) {
return err;
}
});
getToken
向 Auth0 API 请求信息的外部函数
从外部模块导入的getToken
函数如下:
import axios from 'axios'
var options = {
url: //url goes here,
form:
{
// form object goes here
},
json: true
};
const getToken = async () => {
try {
const tokenRequest = await axios.post(options.url, options.form)
return tokenRequest.data.access_token
} catch (err){
return err
}
}
export default getToken;
问题
运行我的测试后,它们会按预期运行,直到 Jest 的--detectOpenHandles
配置检测到以下两个打开的句柄:
Jest has detected the following 2 open handles potentially keeping Jest from exiting:
● TLSWRAP
60 | case 0:
61 | _a.trys.push([0, 2, , 3]);
> 62 | return [4 /*yield*/, axios_1.default.post(options.url, options.form)
| ^
63 | ];
64 | case 1:
at RedirectableRequest.Object.<anonymous>.RedirectableRequest._performRequest (node_modules/follow-redirects/index.js:265:24)
at new RedirectableRequest (node_modules/follow-redirects/index.js:61:8)
at Object.request (node_modules/follow-redirects/index.js:456:14)
at dispatchHttpRequest (node_modules/axios/lib/adapters/http.js:202:25)
at httpAdapter (node_modules/axios/lib/adapters/http.js:46:10)
at dispatchRequest (node_modules/axios/lib/core/dispatchRequest.js:53:10)
at Axios.request (node_modules/axios/lib/core/Axios.js:108:15)
at Axios.<computed> [as post] (node_modules/axios/lib/core/Axios.js:140:17)
at Function.post (node_modules/axios/lib/helpers/bind.js:9:15)
at call (dist/helpers/getToken.js:62:54)
at step (dist/helpers/getToken.js:33:23)
at Object.next (dist/helpers/getToken.js:14:53)
at dist/helpers/getToken.js:8:71
at __awaiter (dist/helpers/getToken.js:4:12)
at Object.token (dist/helpers/getToken.js:56:34)
at call (dist/test/api.test.js:87:48)
at step (dist/test/api.test.js:52:23)
at Object.next (dist/test/api.test.js:33:53)
at dist/test/api.test.js:27:71
at __awaiter (dist/test/api.test.js:23:12)
at dist/test/api.test.js:72:32
● TLSWRAP
141 | switch (_a.label) {
142 | case 0: return [4 /*yield*/, api
> 143 | .get("/interview/1")
| ^
144 | .set("Authorization", "Bearer " + authToken)
145 | .expect(200)];
146 | case 1:
at Test.Object.<anonymous>.Test.serverAddress (node_modules/supertest/lib/test.js:61:33)
at new Test (node_modules/supertest/lib/test.js:38:12)
at Object.get (node_modules/supertest/index.js:27:14)
at call (dist/test/api.test.js:143:26)
at step (dist/test/api.test.js:52:23)
at Object.next (dist/test/api.test.js:33:53)
at dist/test/api.test.js:27:71
at __awaiter (dist/test/api.test.js:23:12)
at Object.<anonymous> (dist/test/api.test.js:139:70)
我确定错误来自这个getToken
异步函数。
为什么我不嘲笑这个功能?
您可能想知道为什么我不嘲笑该函数,正如我之前所说,当 Auth0 使用令牌响应时(顺便说一下,它经常刷新),它还会响应有关用户的信息,并且该信息超出了response.body
. 事实上,它与body
. 所以,如果我想模拟这个函数,我必须在一侧设置 Authorization 标头和不记名令牌(这对于 Supertest 很容易做到),user
另一侧是 Auth0 提供的信息;但是这最后一步是不可能的(至少据我所知;否则,您如何user
在与主体相同的层次结构级别而不是在其中设置信息属性?)
我尝试过的事情
我尝试在测试中添加更长的超时时间beforeAll()
;我尝试添加done
回调而不是使用async/await
insidebeforeAll()
和其他一些不太重要的东西,但它们都没有解决打开句柄问题。事实上,我已经检查了对 Auth0 API 的请求过程是否在响应后关闭,并且有效地关闭了连接,但在运行测试后我仍然收到打开句柄错误。
任何想法将不胜感激!
解决方案
我今天也一直在努力解决类似的问题,未能找到明确的解决方案,但找到了解决方法。解决方法(由alfreema发布)是在调用之前添加以下行axios.post
:
await process.nextTick(() => {});
这似乎允许 Axios 完成其内部管理并准备好跟踪之后打开的新连接。这只是我的猜测,我希望其他人能对此有所了解并提供适当的解决方案。
推荐阅读
- html - Eclipse缩进html内容之间
- /li> 标签
- c++ - Vulkan 正确的帧同步
- bitbucket - 让 BitBucket 服务器通过不同的名称识别自述文件
- git - 适用于团队的 GIT - 团队无需部署到应用程序即可查看分支中的更改
- excel - 应用程序退出指令后显示错误 91
- lua - 如何在游戏中找到 roblox 地点的游戏 ID?
- ruby - 为什么多次构建相同的 gemspec 会创建不同的 gem 二进制文件?
- arrays - 循环嵌套数组时如何正确使用类型保护?
- python - 如何超时以使用硒加载页面?
- python - 将数据框拆分为 3 个大小相同的新数据框 - Pandas