如何实现间谍模块导出实用功能



在javascript (ES6)中,我有一个实用程序模块,它只包含一些函数,然后在文件的末尾,我像这样导出它们:

module.exports = {
  someFunction1,
  someFunction2,
  someFunction3,
}

然后我要为这些函数编写单元测试。有些功能是相互依赖的;它们以某种方式相互调用,例如,someFunction1可能会调用someFunction2。没有循环问题。

一切都运行良好,直到我需要监视其中一个函数被调用。我该怎么做呢?目前我正在使用Chai和Sinon。

在测试文件中,我将整个文件作为一个模块导入:
const wholeModule = require('path/to/the/js/file')

最后,我的测试如下所示:

it('should call the someFunction2', (done) => {
  const spy = sinon.spy(wholeModule, 'someFunction2')
  wholeModule.someFunction1() // someFunction2 is called inside someFunction1
  assert(spy.calledOnce, 'someFunction2 should be called once')
  done()
})

问题是,测试失败了,因为在someFunction1中,直接使用了someFunction2函数。我将间谍应用到模块对象的函数中。但那是一个不同的物体。下面是一个someFunction1:

的例子
function someFunction1() {
  someFunction2()
  return 2;
}

我知道它不起作用的原因,但我不知道在这种情况下使它起作用的最佳实践是什么?请帮助!

您可以使用rewire模块。下面是一个例子:

源代码:

function someFunction1() {
  console.log('someFunction1 called')
  someFunction2();
}
function someFunction2() {
  console.log('someFunction2 called')
}
module.exports = {
  someFunction1: someFunction1,
  someFunction2: someFunction2
}
测试用例:

'use strict';
var expect = require('chai').expect;
var rewire = require('rewire');
var sinon = require('sinon');
var funcs = rewire('../lib/someFunctions');
it('should call the someFunction2', () => {
  var someFunction2Stub = sinon.stub();
  funcs.__set__({
    someFunction2: someFunction2Stub,
  });
  someFunction2Stub.returns(null);
  funcs.someFunction1();
  expect(someFunction2Stub.calledOnce).to.equal(true);
});

正如您已经意识到的那样,发生这种情况是因为您正在存根导出的引用而不是模块中的实际方法。它可以在任何模块中工作,包括你要替换的那个,因为它们包括导出的引用,但当在同一个模块中,它只是调用本地函数。

我发现最简单的解决方案就是调用引用:

function someFunction1() {
  this.someFunction2()
  return 2;
}

最新更新