如何在NodeJS中测试递归调用的函数?

时间:2015-12-21 21:39:07

标签: javascript node.js testing recursion mocha

我有一个用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 
  }
}

问题是源代码正在被更改以适应测试。有更好,更清洁的解决方案吗?

3 个答案:

答案 0 :(得分:1)

我不确定你为什么要使用递归解决方案而不是迭代解决方案 - 但是如果没有其他原因可以更容易迭代地编写它,那么你就不会破坏堆栈:

  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);

没有通过翻译测试或运行 - 但你明白了。

然后在您的测试模式下 - 只提供测试用户。因为您可能需要编写使用对用户的引用的测试。

答案 1 :(得分:1)

有几个库可用于测试与时间相关的事件。据我所知,最常见的解决方案是Lolex - https://github.com/sinonjs/lolex,是Sinon项目的早期部分。 Lolex的问题在于它同步转发定时器,因此忽略了诸如本机节点承诺或process.nextTick之类的事件(虽然它确实假冒setImmediate) - 因此你可能会遇到一些令人讨厌的问题。注意外部库 - 例如,bluebird缓存初始setImmediate,因此您需要以某种方式手动处理它。

另一个选择是Zurvan - https://github.com/Lewerow/zurvan(免责声明:我写了它)。它比Lolex更难解决,因为它大量使用promises,但在存在微队任务(process.nextTick,native Promise)时表现正常,并且有蓝鸟的内置兼容性选项。

这两个库都允许您使用arbirary长度过期与时间相关的事件,并覆盖Date个实例(zurvan也会覆盖process.uptimeprocess.hrtime。如果在测试中执行实际的异步IO,则它们都不安全。

答案 2 :(得分:1)

我已经写了一个例子,说明如何在这里测试你的递归调用函数:

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,但相同的测试前提将适用于您的完整方法。另请注意,您还需要存根延迟方法。