为什么我承诺的存根方法没有解决?
Why my promisifed stub method doesn't resolve?
我有这个认证中间件:
import { promisify } from 'util';
async function verifyToken(req, res, next) {
const [type, token] = req.headers.authorization.split(' ');
try {
const decoded = await promisify(jwt.verify)(token, process.env.SECRET);
req.userId = decoded.id;
return next();
} catch (error) {
return res.status(401).json({ message: 'Invalid token.' });
}
}
这就是我测试它的方式:
it('should return next and inject user id into the request', async () => {
req.headers.authorization = 'Bearer 1234577920fsdaf';
sandbox.stub(jwt, 'verify').resolves({ id: 'bj435çsfkj' });
await verifyToken(req, res, next);
expect(req.userId).to.equal('bj435çsfkj');
expect(next.calledOnce).to.be.true;
});
但是,在我看来,诺言并没有解决。当我 运行 测试时,我在那次测试中得到 Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves
。也许和promisify
有关?我怀疑它确实如此,因为我已经标记了 return 承诺的许多其他函数,但这是第一个引发此类错误的函数。
在这种情况下,我如何正确存根 jwt.verify
?
编辑:
jwt.verify
来自 jsonwebtoken 包并具有以下签名:
jwt.verify(token, secretOrPublicKey, [options, callback])
单元测试解决方案:
index.ts
:
import { promisify } from "util";
import jwt from "jsonwebtoken";
async function verifyToken(req, res, next) {
const [type, token] = req.headers.authorization.split(" ");
try {
// @ts-ignore
const decoded = await promisify(jwt.verify)(token, process.env.SECRET || "");
// @ts-ignore
// eslint-disable-next-line require-atomic-updates
req.userId = decoded.id;
return next();
} catch (error) {
return res.status(401).json({ message: "Invalid token." });
}
}
export { verifyToken };
index.test.ts
:
import { verifyToken } from ".";
import sinon from "sinon";
import jwt from "jsonwebtoken";
import { expect } from "chai";
const sandbox = sinon.createSandbox();
describe("63795862", () => {
afterEach(() => {
sandbox.restore();
});
it("should return next and inject user id into the request", async () => {
const req = { userId: "", headers: { authorization: "Bearer 1234577920fsdaf" } };
const res = {};
const next = sandbox.stub();
const verifyStub = sandbox.stub(jwt, "verify").callsFake((token, secretOrPublicKey, callback: any) => {
callback(null, { id: "bj435çsfkj" });
});
await verifyToken(req, res, next);
sandbox.assert.calledWithExactly(verifyStub, "1234577920fsdaf", "", sinon.match.func);
expect(req.userId).to.equal("bj435çsfkj");
expect(next.calledOnce).to.be.true;
});
it("should return 401 status error", async () => {
const req = { userId: "", headers: { authorization: "Bearer 1234577920fsdaf" } };
const res = { status: sandbox.stub().returnsThis(), json: sandbox.stub() };
const next = sandbox.stub();
const mError = new Error("invalid token");
sandbox.stub(jwt, "verify").callsFake((token, secretOrPublicKey, callback: any) => {
callback(mError);
});
await verifyToken(req, res, next);
expect(req.userId).to.equal("");
sinon.assert.calledWithExactly(res.status, 401);
sinon.assert.calledWithExactly(res.json, { message: "Invalid token." });
});
});
带有覆盖率报告的单元测试结果:
63795862
✓ should return next and inject user id into the request
✓ should return 401 status error
2 passing (24ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.test.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
源代码:https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/Whosebug/63795862
我有这个认证中间件:
import { promisify } from 'util';
async function verifyToken(req, res, next) {
const [type, token] = req.headers.authorization.split(' ');
try {
const decoded = await promisify(jwt.verify)(token, process.env.SECRET);
req.userId = decoded.id;
return next();
} catch (error) {
return res.status(401).json({ message: 'Invalid token.' });
}
}
这就是我测试它的方式:
it('should return next and inject user id into the request', async () => {
req.headers.authorization = 'Bearer 1234577920fsdaf';
sandbox.stub(jwt, 'verify').resolves({ id: 'bj435çsfkj' });
await verifyToken(req, res, next);
expect(req.userId).to.equal('bj435çsfkj');
expect(next.calledOnce).to.be.true;
});
但是,在我看来,诺言并没有解决。当我 运行 测试时,我在那次测试中得到 Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves
。也许和promisify
有关?我怀疑它确实如此,因为我已经标记了 return 承诺的许多其他函数,但这是第一个引发此类错误的函数。
在这种情况下,我如何正确存根 jwt.verify
?
编辑:
jwt.verify
来自 jsonwebtoken 包并具有以下签名:
jwt.verify(token, secretOrPublicKey, [options, callback])
单元测试解决方案:
index.ts
:
import { promisify } from "util";
import jwt from "jsonwebtoken";
async function verifyToken(req, res, next) {
const [type, token] = req.headers.authorization.split(" ");
try {
// @ts-ignore
const decoded = await promisify(jwt.verify)(token, process.env.SECRET || "");
// @ts-ignore
// eslint-disable-next-line require-atomic-updates
req.userId = decoded.id;
return next();
} catch (error) {
return res.status(401).json({ message: "Invalid token." });
}
}
export { verifyToken };
index.test.ts
:
import { verifyToken } from ".";
import sinon from "sinon";
import jwt from "jsonwebtoken";
import { expect } from "chai";
const sandbox = sinon.createSandbox();
describe("63795862", () => {
afterEach(() => {
sandbox.restore();
});
it("should return next and inject user id into the request", async () => {
const req = { userId: "", headers: { authorization: "Bearer 1234577920fsdaf" } };
const res = {};
const next = sandbox.stub();
const verifyStub = sandbox.stub(jwt, "verify").callsFake((token, secretOrPublicKey, callback: any) => {
callback(null, { id: "bj435çsfkj" });
});
await verifyToken(req, res, next);
sandbox.assert.calledWithExactly(verifyStub, "1234577920fsdaf", "", sinon.match.func);
expect(req.userId).to.equal("bj435çsfkj");
expect(next.calledOnce).to.be.true;
});
it("should return 401 status error", async () => {
const req = { userId: "", headers: { authorization: "Bearer 1234577920fsdaf" } };
const res = { status: sandbox.stub().returnsThis(), json: sandbox.stub() };
const next = sandbox.stub();
const mError = new Error("invalid token");
sandbox.stub(jwt, "verify").callsFake((token, secretOrPublicKey, callback: any) => {
callback(mError);
});
await verifyToken(req, res, next);
expect(req.userId).to.equal("");
sinon.assert.calledWithExactly(res.status, 401);
sinon.assert.calledWithExactly(res.json, { message: "Invalid token." });
});
});
带有覆盖率报告的单元测试结果:
63795862
✓ should return next and inject user id into the request
✓ should return 401 status error
2 passing (24ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.test.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
源代码:https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/Whosebug/63795862