异步外部函数留下开放句柄 - Jest、Supertest、Express

Mau*_*ani 13 node.js express supertest jestjs

我开始使用 Jest 和 Supertest(针对端点)测试我的应用程序。测试工作顺利,但Jest 在运行测试后检测到 2 个打开的句柄,这会阻止 Jest 干净退出。

\n

这个打开的句柄是由我的测试文件中调用的外部异步函数生成的。我正在使用外部函数从 Auth0 API 请求 JWT 令牌;但对 Auth0 的请求还在其响应中提供了传递端点中间件的关键信息(有关此的更多信息如下)。这里要记住两件事:

\n
    \n
  1. 到目前为止,我无法避免从 Auth0 请求令牌,因为正如我所说,该响应还包含一个user具有关键信息的对象。Auth0 将此对象设置在主体响应之外的同一级别,但不在其中。该信息是传递端点中间件的关键。
  2. \n
  3. 我已经隔离了所有错误,以确保仅当我调用从 Auth0 API 请求令牌和用户信息的外部异步函数时才会出现问题;该问题是通过使用getToken测试文件中的该函数(称为 )生成的。
  4. \n
\n

测试文件代码

\n
import app from "../app";\nimport mongoose from "mongoose";\nimport supertest from "supertest";\nimport { getToken } from "../helpers";\nimport dotenv from "dotenv";\nimport * as config from "../config";\n\ndotenv.config();\n\nconst api = supertest(app);\n\nlet authToken: any;\nlet db: any;\n\nbeforeAll(async() => {\n  try {\n    mongoose.connect(config.MONGODB_URI, {\n      useNewUrlParser: true,\n      useUnifiedTopology: true,\n      useCreateIndex: true,\n    });\n    db = mongoose.connection;\n    db.on("error", console.error.bind(console, "Console Error:"));\n    db.once("open", () =>\n      console.log(`App connected to "${db.name}" database`)\n    );\n    authToken = await getToken()\n  } catch (err) {\n    return err\n  }\n});\n\ndescribe("GET /interview/:idCandidate", () => {\n  test("With auth0 and read permissions", async () => {\n       await api\n        .get("/interview/1")\n        .set("Authorization", "Bearer " + authToken)\n        .expect(200)\n  });\n});\n\nafterAll(async () => {\n  try {\n    await db.close();\n  } catch (err) {\n    return err;\n  }\n});\n
Run Code Online (Sandbox Code Playgroud)\n

getToken向 Auth0 API 请求信息的外部函数

\n

从外部模块导入的函数getToken如下:

\n
import axios from 'axios'\n\nvar options = {\n    url: //url goes here,\n    form:\n    {\n      // form object goes here\n    },\n    json: true\n  };\n  \n  const getToken = async () => {\n    try {\n      const tokenRequest = await axios.post(options.url, options.form)\n      return tokenRequest.data.access_token\n    } catch (err){\n      return err\n    }\n  } \n\n\nexport default getToken;\n
Run Code Online (Sandbox Code Playgroud)\n

问题

\n

一旦运行我的测试,它们就会按预期运行,直到 Jest 的--detectOpenHandles配置检测到以下两个打开的句柄:

\n
Jest has detected the following 2 open handles potentially keeping Jest from exiting:\n\n  \xe2\x97\x8f  TLSWRAP\n\n      60 |             case 0:\n      61 |                 _a.trys.push([0, 2, , 3]);\n    > 62 |                 return [4 /*yield*/, axios_1.default.post(options.url, options.form)\n         |                                                      ^\n      63 |                 ];  \n      64 |             case 1:    \n\n      at RedirectableRequest.Object.<anonymous>.RedirectableRequest._performRequest (node_modules/follow-redirects/index.js:265:24)\n      at new RedirectableRequest (node_modules/follow-redirects/index.js:61:8)\n      at Object.request (node_modules/follow-redirects/index.js:456:14)\n      at dispatchHttpRequest (node_modules/axios/lib/adapters/http.js:202:25)\n      at httpAdapter (node_modules/axios/lib/adapters/http.js:46:10)\n      at dispatchRequest (node_modules/axios/lib/core/dispatchRequest.js:53:10)\n      at Axios.request (node_modules/axios/lib/core/Axios.js:108:15)\n      at Axios.<computed> [as post] (node_modules/axios/lib/core/Axios.js:140:17)\n      at Function.post (node_modules/axios/lib/helpers/bind.js:9:15)\n      at call (dist/helpers/getToken.js:62:54)\n      at step (dist/helpers/getToken.js:33:23)\n      at Object.next (dist/helpers/getToken.js:14:53)\n      at dist/helpers/getToken.js:8:71\n      at __awaiter (dist/helpers/getToken.js:4:12)\n      at Object.token (dist/helpers/getToken.js:56:34)\n      at call (dist/test/api.test.js:87:48)\n      at step (dist/test/api.test.js:52:23)\n      at Object.next (dist/test/api.test.js:33:53)\n      at dist/test/api.test.js:27:71\n      at __awaiter (dist/test/api.test.js:23:12)\n      at dist/test/api.test.js:72:32\n\n\n  \xe2\x97\x8f  TLSWRAP\n\n      141 |             switch (_a.label) {\n      142 |                 case 0: return [4 /*yield*/, api\n    > 143 |                         .get("/interview/1")\n          |                          ^\n      144 |                         .set("Authorization", "Bearer " + authToken)\n      145 |                         .expect(200)];\n      146 |                 case 1:\n\n      at Test.Object.<anonymous>.Test.serverAddress (node_modules/supertest/lib/test.js:61:33)\n      at new Test (node_modules/supertest/lib/test.js:38:12)\n      at Object.get (node_modules/supertest/index.js:27:14)\n      at call (dist/test/api.test.js:143:26)\n      at step (dist/test/api.test.js:52:23)\n      at Object.next (dist/test/api.test.js:33:53)\n      at dist/test/api.test.js:27:71\n      at __awaiter (dist/test/api.test.js:23:12)\n      at Object.<anonymous> (dist/test/api.test.js:139:70)\n
Run Code Online (Sandbox Code Playgroud)\n

我确信错误来自这个getToken异步函数。

\n

为什么我不嘲笑这个函数?

\n

您可能想知道为什么我不嘲笑该函数,正如我之前所说,当 Auth0 使用令牌响应(顺便说一下,它经常刷新)时,它还会响应有关用户的信息,并且该信息超出了response.body. 事实上,它与body. 所以,如果我想模拟这个函数,我必须在一侧设置带有不记名令牌的授权标头(这很容易用 Supertest 完成),user在另一侧设置 Auth0 提供的信息;但这最后一步是不可能的(至少据我所知;否则,如何user在与正文相同的层次结构级别而不是在其中设置信息属性?)

\n

我尝试过的事情

\n

我尝试为测试添加更长的超时beforeAll();我尝试添加done回调而不是async/await在内部使用beforeAll()和其他一些不太重要的事情,但它们都没有解决打开句柄问题。事实上,我已经检查了对 Auth0 API 的请求过程是否在响应后关闭,并且实际上该连接关闭,但在运行测试后我仍然收到打开句柄错误。

\n

任何想法将不胜感激!

\n

Roc*_*etR 8

今天我也遇到了类似的问题,未能找到明确的解决方案,但找到了解决方法。解决方法(由alfreema发布)是在调用之前添加以下行axios.post

await process.nextTick(() => {});
Run Code Online (Sandbox Code Playgroud)

这似乎让 Axios 完成了它的内务处理,并准备好跟踪随后打开的新连接。这只是我的猜测,我希望其他人能够对此有更多的了解并提供适当的解决方案。

  • 有谁知道为什么这有效?我在调用被测函数(调用 axios 的函数)之前放置了它,并且我不再收到错误... (2认同)