使用 mocha 测试 express 服务器并使用异步初始化程序进行 supertest 调用请求两次
Posted
技术标签:
【中文标题】使用 mocha 测试 express 服务器并使用异步初始化程序进行 supertest 调用请求两次【英文标题】:Testing express server with mocha and supertest with async initializer calls the request twice 【发布时间】:2020-10-27 16:00:09 【问题描述】:我正在尝试为快速服务器建立一个测试框架,并且需要异步初始化的测试给我带来了问题。最初我尝试使用磁带和超测试,但由于磁带似乎对异步操作的整体支持很差,我改用 mocha。现在我至少收到了一些错误消息,但测试仍然无法正常工作。
我正在尝试测试一个虚拟 ping 端点 /auth_ping,它应该需要一个有效的令牌来响应 pong,否则应该响应 403。目前身份验证尚未实现,所以我正在尝试设置一个最初失败的测试,因为服务器在没有令牌的情况下响应 200 而不是 403。
我的第一次尝试是这样的
'use strict';
const request = require('supertest');
const app = require('../server');
const keycloak = require('./util/keycloak-mock');
describe("authenticated ping", function()
it("should respond 403 when no token is provided", function(done)
keycloak.fetchToken()
.then((token) => request(app)
.get("/auth_ping")
.expect(403)
.end(function (err, res)
if (err)
done(err)
;
done();
));
);
);
测试用例从通过异步调用从模拟身份验证器获取有效令牌开始。在此测试中,未使用令牌,但我还是想进行调用以确保已初始化模拟身份验证器。无论如何,令牌会在进一步的测试用例中使用,因此获取需要工作。
我使用这个测试得到的输出如下
authenticated ping
Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises
Warning: .end() was called twice. This is not supported in superagent
superagent: double callback bug
(node:21) UnhandledPromiseRejectionWarning: SyntaxError: Unexpected token in JSON at position 29
at JSON.parse (<anonymous>)
at IncomingMessage.res.on (/usr/src/app/node_modules/superagent/lib/node/parsers/json.js:11:35)
at IncomingMessage.emit (events.js:203:15)
at endReadableNT (_stream_readable.js:1145:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
(node:21) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:21) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
1) should respond 403 when no token is provided
0 passing (2s)
1 failing
1) authenticated ping
should respond 403 when no token is provided:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/usr/src/app/test/index.js)
我不太确定这里的流程是如何工作的,以及为什么会发出两次请求。是的,我同时调用 then() 和 end(),但是因为 then() 与超测无关,我不知道它如何影响请求。另外 end() 似乎是我为 mocha 调用 done() 的唯一地方,所以我看不出没有它测试如何工作。
无论如何,我尝试通过删除 end() 调用来修改测试:
'use strict';
const request = require('supertest');
const app = require('../server');
const keycloak = require('./util/keycloak-mock');
describe("authenticated ping", function()
it("should respond 403 when no token is provided", function(done)
keycloak.fetchToken()
.then((token) => request(app)
.get("/auth_ping")
.expect(403)
);
);
);
之后的输出是这样的:
authenticated ping
(node:21) UnhandledPromiseRejectionWarning: Error: expected 403 "Forbidden", got 200 "OK"
at Test._assertStatus (/usr/src/app/node_modules/supertest/lib/test.js:268:12)
at Test._assertFunction (/usr/src/app/node_modules/supertest/lib/test.js:283:11)
at Test.assert (/usr/src/app/node_modules/supertest/lib/test.js:173:18)
at Server.localAssert (/usr/src/app/node_modules/supertest/lib/test.js:131:12)
at Object.onceWrapper (events.js:286:20)
at Server.emit (events.js:198:13)
at emitCloseNT (net.js:1619:8)
at process._tickCallback (internal/process/next_tick.js:63:19)
(node:21) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:21) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
1) should respond 403 when no token is provided
0 passing (2s)
1 failing
1) authenticated ping
should respond 403 when no token is provided:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/usr/src/app/test/index.js)
在堆栈跟踪中,我可以看到达到了预期的测试结果,因为将响应 200 与预期的 403 进行了比较,该部分现在看起来很好,但之后测试用例超时并因此而失败。我认为这是意料之中的,因为从未调用过done()
,我觉得我明白这里发生了什么。
但是在我的第一次尝试中会发生什么以及如何解决这个问题?它看起来比后一种尝试更有效,但我不明白为什么请求被发送两次?
【问题讨论】:
【参考方案1】:我相信我现在已经解决了这个问题,它与异步调用无关。
方法的正确写法似乎是
'use strict';
const request = require('supertest');
const app = require('../server');
const keycloak = require('./util/keycloak-mock');
describe("authenticated ping", function()
it("should respond 403 when no token is provided", function(done)
keycloak.fetchToken()
.then((token) => request(app)
.get("/auth_ping")
.expect(403)
.end(done);
);
);
);
似乎我发现的关于在 mocha 中使用 supertest 的简短教程包括建议将我自己的方法添加到 end() 函数中可能是一种不好的做法。这在测试成功时可以正常工作,但如果测试在期望之一上失败,则会中断。
我没有在任何地方找到它,但我测试过的似乎你必须以 end(done) 结束命令链或将 done 作为附加参数添加到最后期望在链中(但不是任何早期的)。因此,除了添加 end(done) 之外,我还可以将其省略并以 expect(403, done) 结束链。现在我更喜欢显式结束,因为这样更容易在不破坏任何东西的情况下向链中添加额外的期望。
【讨论】:
以上是关于使用 mocha 测试 express 服务器并使用异步初始化程序进行 supertest 调用请求两次的主要内容,如果未能解决你的问题,请参考以下文章
在 Mocha 测试中使用 Superagent/Supertest 和 Express 应用程序
使用 Express、Socket.io 和 Node-Telegram-Bot-Api 结束 Mocha 测试
单元/集成测试 Express REST API, mongoose, mocha, sinon, chai, supertest
Node Express Mocha 测试:TypeError: chai.request is not a function