Javascript 开玩笑不';无法使用util.promisify(设置超时)
我知道有很多类似的问题,但我相信我的问题是不同的,没有任何当前的答案回答 我正在Express.JS中测试REST API。下面是一个简单的工作示例和几个不同编号的测试用例Javascript 开玩笑不';无法使用util.promisify(设置超时),javascript,node.js,jestjs,es6-promise,Javascript,Node.js,Jestjs,Es6 Promise,我知道有很多类似的问题,但我相信我的问题是不同的,没有任何当前的答案回答 我正在Express.JS中测试REST API。下面是一个简单的工作示例和几个不同编号的测试用例 const express = require("express"); let request = require("supertest"); const { promisify } = require("util"); const app = express(); request = request(app); cons
const express = require("express");
let request = require("supertest");
const { promisify } = require("util");
const app = express();
request = request(app);
const timeOut = promisify(setTimeout);
const timeOut2 = time =>
new Promise(resolve => {
setTimeout(resolve, time);
});
app.locals.message = "Original string";
app.get("/one", async (req, res) => {
await timeOut(1000);
res.send(app.locals.message);
});
app.get("/two", (req, res) => {
res.send(app.locals.message);
});
app.get("/three", async (req, res) => {
await timeOut2(1000);
res.send(app.locals.message);
});
test("1. test promisify", async () => {
expect.assertions(1);
const response = await request.get("/one");
expect(response.text).toEqual("Original string");
});
test("2. test promisify with fake timers", () => {
expect.assertions(1);
jest.useFakeTimers();
request.get("/one").then(res => {
expect(res.text).toEqual("Original string");
});
jest.runAllTimers();
});
test("3. test promisify with fake timers and returning pending promise", () => {
expect.assertions(1);
jest.useFakeTimers();
const response = request.get("/one").then(res => {
expect(res.text).toEqual("Original string");
});
jest.runAllTimers();
return response;
});
test("4. test no timeout", async () => {
expect.assertions(1);
const response = await request.get("/two");
expect(response.text).toEqual("Original string");
});
test("5. test custom timeout", async () => {
expect.assertions(1);
const response = await request.get("/three");
expect(response.text).toEqual("Original string");
});
test("6. test custom timeout with fake timers", () => {
expect.assertions(1);
jest.useFakeTimers();
const response = request.get("/three").then(res => {
expect(res.text).toEqual("Original string");
});
jest.runAllTimers();
return response;
});
在虚拟环境中运行测试表明只有测试5通过。
那么我的第一个问题是为什么测试5通过而不是测试1,因为它们是完全相同的测试,而不是基于承诺的延迟的不同实现。
这两种实现在Jest测试之外都能完美地工作(使用Supertest测试而不使用Jest)
虽然测试5通过了,但它使用的是实时定时器,因此并不理想。就我所见,测试6应该是伪计时器(我还尝试了一个在然后正文中调用done()的版本),但这也失败了
我的web应用程序有一个使用util.promisify(setTimeout)
的处理程序的路由,因此,Jest试图测试它,即使是使用实时程序,这一事实使该框架对我的用处大大降低。考虑到定制实现(测试5)确实有效,这似乎是一个bug
尽管如此,Jest仍然无法使用模拟计时器在测试6上工作,因此即使我重新实现我的应用程序中的延迟(我不想这样做),我仍然必须忍受无法加速的缓慢测试
这两个问题中有一个是预期行为吗?如果不是,我做错了什么?这是一个有趣的问题。它一直到核心内置函数的实现
为什么测试5通过而测试1不通过
这花了一段时间才追上
和jsdom
提供
在jsdom
测试环境中调用promisify(setTimeout)
将返回在jsdom
提供的setTimeout
上运行创建的函数
相反,如果Jest
正在节点中运行,则调用promisify(setTimeout)
只返回
这个简单的测试通过了节点
测试环境,但挂起在jsdom
:
const{promisify}=require('util');
测试('promisify(setTimeout)',()=>{
返回promisify(setTimeout)(0)。然后(()=>{
期望(真),期望(真);
});
});
结论:由jsdom
提供的setTimeout
的promisify
-ed版本不起作用
如果在节点
测试环境中运行,测试1和测试5都通过
使用promisify(setTimeout)
和计时器模拟的测试代码
听起来真正的问题是如何使用以下工具测试这样的代码:
app.js
const express=require(“express”);
const{promisify}=require(“util”);
常量app=express();
const timeOut=promisify(设置超时);
app.locals.message=“原始字符串”;
app.get(“/one”,异步(请求,res)=>{
等待超时(10000);//等待10秒
res.send(app.locals.message);
});
导出默认应用程序;
我花了一段时间才弄明白,我将逐一介绍每个部分
模拟promisify(设置超时)
在没有模拟的情况下,无法使用计时器模拟测试使用promisify(setTimeout)
的代码promisify(setTimeout)
:
- 在
jsdom
环境中promisify(setTimeout)
将挂起
- 在
节点
环境中promisify(setTimeout)
将是一个不调用setTimeout
的函数,因此当它没有任何效果时
promisify(setTimeout)
通过创建以下\uuuuumock\uuuuu/util.js
:
const util=require.requireActual('util');//得到真实的信息
const realPromisify=util.promisify;//抓住真正的承诺
util.promisify=(…参数)=>{
if(args[0]==setTimeout){//返回模拟if promisify(setTimeout)
返回时间=>
新承诺(解决=>{
setTimeout(解析,时间);
});
}
返回realPromisify(…args);/…否则调用realPromisify
}
module.exports=util;
注意调用jest.mock('util')代码>在测试中
按一定间隔调用jest.runAllTimers()
事实证明,request.get
在supertest
中启动了一个完整的过程,该过程使用,并且在当前正在运行的消息(测试)完成之前不会运行任何内容
这是有问题的,因为request.get
最终将运行app.get
,然后调用wait timeOut(10000)代码>在调用jest.runAllTimers
之前不会完成
同步测试中的任何内容都将在请求之前运行。如果测试期间运行了jest.runAllTimers
,则get
会执行任何操作,这不会对以后调用等待超时(10000)产生任何影响代码>
此问题的解决方法是设置一个间隔,定期将调用jest.runAllTimers
的JavaScript事件循环中的消息排队。调用的消息等待超时时(10000)代码>运行它将在该行暂停,然后将运行一条调用jest.runAllTimers
的消息,等待的消息将等待超时(10000)然后,代码>将能够继续并且请求。get
将完成
捕获设置间隔和清除间隔
最后一点需要注意的是jest.useFakeTimers
包括setInterval
和clearInterval
,因此为了设置间隔并清除它,我们需要在调用jest.useFakeTimers
之前捕获真正的函数
考虑到所有这些,下面是对上面列出的app.js代码的工作测试:
jest.mock('util');//必须显式模拟core Node.js模块
const supertest=require(‘supertest’);
从“./app”导入应用程序;
骗局