当被测试的函数没有使用 async/await 时,我可以在 Jest 中使用 async/await 吗?
Can I use async/await in Jest, when the tested function is not using async/await?
我有一个用于添加新用户的简单控制器。解析成功(用户添加)后,控制器发送202响应。如您所见,该函数使用 then/catch 而未使用 async/await.
const addUserController = function (req, res, next) {
Users.addOne(req.userid, req.body.email)
.then(() => {
res.status(202).send();
})
.catch((err) => {
console.log(err);
res.status(500).json({ message: "Internal server error." });
});
};
当我在 Jest 中用 the 测试这个函数时,函数立即执行,没有转到 then() 部分,导致错误的 200 代码,而不是 202,所以下面的测试失败了:
it("Should add a user", () => {
let req, res, next, pool;
pool = new Pool();
req = httpsMocks.createRequest();
res = httpsMocks.createResponse();
res.next = null;
req.userid = 1;
req.body = {
id: 2
}
pool.query.mockResolvedValue({rows:[], rowCount: 1});
apiController.addUserController(req, res, next);
expect(res.statusCode).toBe(202);
expect(pool.query).toBeCalledTimes(1);
});
然而,当我这样做的时候:
it("Should add a user", async () => {
let req, res, next, pool;
pool = new Pool();
req = httpsMocks.createRequest();
res = httpsMocks.createResponse();
res.next = null;
req.userid = 1;
req.body = {
id: 2
}
pool.query.mockResolvedValue({rows:[], rowCount: 1});
await apiController.addUserController(req, res, next);
expect(res.statusCode).toBe(202);
expect(pool.query).toBeCalledTimes(1);
});
即我添加 async/await,它工作正常 - 响应状态代码为 202,这意味着函数已等待并且测试通过。
但为什么?当我将鼠标悬停在新添加的 'await' 上时,VS 代码提示
'await' has no effect on the type of this expression.
好吧,它是有道理的——它应该没有效果,因为被测试的函数不是异步的,所以它不应该工作,但是,它工作——只有当我将 async/await 添加到 Jest 时功能正常。
有人可以给我解释一下吗?
I add async/await, it works alright - the response status code is 202, meaning the function was awaited and the test passes. But why?
不,正如您从缺失的 return 值中得出的结论,该函数未等待。您的代码相当于
apiController.addUserController(req, res, next);
await undefined;
现在,为什么它仍然有所作为?因为使用 await
,测试会在 运行 调用 expect()
之前等待一小段时间,而这一小段时间足以让您的模拟池获得 return 一个值并具有第一个 .then()
处理程序被执行。
但是,您现在基本上引入了竞争条件。在 addUserController
中使用更长的承诺链会使测试失败。甚至在 .then().catch()
中创建的 500 状态的测试可能已经不起作用。
这很脆弱,不要写那样的测试。一种解决方案是简单地 return
来自 addUserController
和 await
的 promise 链——只要其他调用者不会被它混淆,这就是简单的解决方法。另一种解决方案是实际等待模拟响应被实际发送。如果我没看错 docs of node-mocks-http
,这样的事情应该可行:
it("Should add a user", async () => {
const { once, EventEmitter } = require('events');
const pool = new Pool();
const req = httpsMocks.createRequest();
const res = httpsMocks.createResponse({ eventEmitter: EventEmitter });
res.next = null;
req.userid = 1;
req.body = {
id: 2
}
pool.query.mockResolvedValue({rows:[], rowCount: 1});
const responseEndPromise = once(res, 'end');
apiController.addUserController(req, res, next);
await responseEndPromise;
expect(res.statusCode).toBe(202);
expect(pool.query).toBeCalledTimes(1);
});
我有一个用于添加新用户的简单控制器。解析成功(用户添加)后,控制器发送202响应。如您所见,该函数使用 then/catch 而未使用 async/await.
const addUserController = function (req, res, next) {
Users.addOne(req.userid, req.body.email)
.then(() => {
res.status(202).send();
})
.catch((err) => {
console.log(err);
res.status(500).json({ message: "Internal server error." });
});
};
当我在 Jest 中用 the 测试这个函数时,函数立即执行,没有转到 then() 部分,导致错误的 200 代码,而不是 202,所以下面的测试失败了:
it("Should add a user", () => {
let req, res, next, pool;
pool = new Pool();
req = httpsMocks.createRequest();
res = httpsMocks.createResponse();
res.next = null;
req.userid = 1;
req.body = {
id: 2
}
pool.query.mockResolvedValue({rows:[], rowCount: 1});
apiController.addUserController(req, res, next);
expect(res.statusCode).toBe(202);
expect(pool.query).toBeCalledTimes(1);
});
然而,当我这样做的时候:
it("Should add a user", async () => {
let req, res, next, pool;
pool = new Pool();
req = httpsMocks.createRequest();
res = httpsMocks.createResponse();
res.next = null;
req.userid = 1;
req.body = {
id: 2
}
pool.query.mockResolvedValue({rows:[], rowCount: 1});
await apiController.addUserController(req, res, next);
expect(res.statusCode).toBe(202);
expect(pool.query).toBeCalledTimes(1);
});
即我添加 async/await,它工作正常 - 响应状态代码为 202,这意味着函数已等待并且测试通过。 但为什么?当我将鼠标悬停在新添加的 'await' 上时,VS 代码提示
'await' has no effect on the type of this expression.
好吧,它是有道理的——它应该没有效果,因为被测试的函数不是异步的,所以它不应该工作,但是,它工作——只有当我将 async/await 添加到 Jest 时功能正常。
有人可以给我解释一下吗?
I add async/await, it works alright - the response status code is 202, meaning the function was awaited and the test passes. But why?
不,正如您从缺失的 return 值中得出的结论,该函数未等待。您的代码相当于
apiController.addUserController(req, res, next);
await undefined;
现在,为什么它仍然有所作为?因为使用 await
,测试会在 运行 调用 expect()
之前等待一小段时间,而这一小段时间足以让您的模拟池获得 return 一个值并具有第一个 .then()
处理程序被执行。
但是,您现在基本上引入了竞争条件。在 addUserController
中使用更长的承诺链会使测试失败。甚至在 .then().catch()
中创建的 500 状态的测试可能已经不起作用。
这很脆弱,不要写那样的测试。一种解决方案是简单地 return
来自 addUserController
和 await
的 promise 链——只要其他调用者不会被它混淆,这就是简单的解决方法。另一种解决方案是实际等待模拟响应被实际发送。如果我没看错 docs of node-mocks-http
,这样的事情应该可行:
it("Should add a user", async () => {
const { once, EventEmitter } = require('events');
const pool = new Pool();
const req = httpsMocks.createRequest();
const res = httpsMocks.createResponse({ eventEmitter: EventEmitter });
res.next = null;
req.userid = 1;
req.body = {
id: 2
}
pool.query.mockResolvedValue({rows:[], rowCount: 1});
const responseEndPromise = once(res, 'end');
apiController.addUserController(req, res, next);
await responseEndPromise;
expect(res.statusCode).toBe(202);
expect(pool.query).toBeCalledTimes(1);
});