如何在NodeJS中测试递归调用的函数
How to test a recursively called function in NodeJS?
我有一个用ES6/7编写的递归函数,它由babel转换。我创建了一个循环函数,使用mongoose检查是否有用户文档。
// Keep checking if there is a user, if there is let execution continue
export async function checkIfUserExists(){
let user = await User.findOneAsync({});
// if there is no user delay one minute and check again
if(user === null){
await delay(1000 * 60 * 1)
return checkIfUserExists()
} else {
// otherwise, if there a user, let the execution move on
return true
}
}
如果没有用户,我将使用delay
库将执行延迟一分钟,然后递归调用函数。
这允许停止执行整个功能,直到找到用户:
async function overallFunction(){
await checkIfUserExists()
// more logic
}
else分支很容易为其生成测试。如何为if分支创建一个测试来验证递归是否正常工作?
目前,我已经在测试过程中用proxyquire替换了延迟方法,将其替换为只返回值的自定义延迟函数。在这一点上,我可以更改代码如下:
// Keep checking if there is a user, if there is let execution continue
export async function checkIfUserExists(){
let user = await User.findOneAsync({});
// if there is no user delay one minute and check again
if(user === null){
let testing = await delay(1000 * 60 * 1)
if (testing) return false
return checkIfUserExists()
} else {
// otherwise, if there a user, let the execution move on
return
}
}
问题是源代码正在更改以适应测试。有更好、更清洁的解决方案吗?
我在这里写了一个如何测试递归调用函数的例子:
https://jsfiddle.net/Fresh/qppprz20/
此测试使用Sinon javascript测试库。您可以在第n次调用时设置存根的行为,因此您可以模拟何时没有返回用户,然后在随后返回用户时,例如
// Stub the method behaviour using Sinon javascript framework
var user = new User();
var userStub = sinon.stub(user, 'findOneAsync');
userStub.onFirstCall().returns(null);
userStub.onSecondCall().returns({});
因此,onFirstCall模拟第一个调用,onSecondCall模拟递归调用。
请注意,在完整的示例中,我简化了checkIfUserExists,但相同的测试前提将适用于您的完整方法。还要注意的是,您还必须截断您的延迟方法。
有几个库可以用于测试与时间相关的事件。据我所知,最常见的解决方案是Lolex-https://github.com/sinonjs/lolex,Sinon项目的早期部分。Lolex的问题是,它同步转发计时器,从而忽略诸如本机节点承诺或process.nextTick
之类的事件(尽管它确实正确地伪造了setImmediate
),因此您可能会遇到一些棘手的问题。对于外部库要小心——例如,bluebird
缓存初始的setImmediate
,因此您需要以某种方式手动处理它。
另一种选择是Zurvanhttps://github.com/Lewerow/zurvan(免责声明:我写的)。它比Lolex更难处理,因为它大量使用promise,但在微队列任务(process.nextTick
,原生Promise
)存在时表现良好,并且有一个内置的蓝鸟兼容性选项。
这两个库都允许您在arbirary长度上过期与时间相关的事件,并覆盖Date
实例(zurvan也覆盖process.uptime
和process.hrtime
)。如果在测试中执行实际的异步IO,那么这两种方法都不安全。
我不知道你为什么要使用递归解决方案而不是迭代解决方案,但如果没有其他原因,你不会破坏堆栈,迭代编写它可能会更容易:
do{
let user = await User.findOneAsync({});
// if there is no user delay one minute and check again
if(user === null){
await delay(1000 * 60 * 1);
}
else{
return true;
}
}while (!user);
还没有通过解释器测试或运行过这个,但你已经明白了。
然后在测试模式中,只需提供一个测试用户。因为您可能需要编写使用对用户的引用的测试。
- 为什么setTimeout适用于无休止的递归调用
- 有没有一种方法可以用步骤递归调用jQuery animate()
- 在Dojo类中递归调用setTimeout时是否存在内存泄漏
- 递归调用异步函数
- 递归调用相同的函数
- 计算一个函数在JS中被递归调用的次数
- 如何在 Javascript 中递归调用存储在变量中的函数
- 如果回调调用封闭函数,它是否被称为递归调用
- 无法从响应的结束事件中对 Http.Request 进行递归调用
- JavaScript中的递归调用不起作用
- KO 计算递归调用
- 删除数组递归调用模式中的项
- 无法在 Javascript 中进行递归调用
- 如何递归调用承诺函数
- Javascript 堆栈和递归调用
- 无法在 javascript 中使用 settimeout 递归调用函数
- 原型函数中的递归调用
- 在 Javascript 中递归调用“父”构造函数
- AngularJS:使用$timeout递归调用函数时的RangeError
- JavaScript 理解递归调用代码清理中的上下文,没有“with”关键字