即使被监听的功能,Sinon间谍程序也会失败

时间:2019-01-08 15:06:13

标签: node.js mocha sinon chai

即使在测试期间确实调用了我所监视的功能(我通过一些简单的控制台日志记录就证明了这一点),我仍然无法理解为什么sinon间谍对我而言失败了。

所以说我有以下内容:

index.js

let MyModule = require('./src/index.js');

MyModule = new MyModule();

module.exports = {
  DoStuff: MyModule.DoStuff,
  doOtherStuff: MyModule.doOtherStuff,
};

src / index.js

const MyModule = function MyModule() {

  const self = this;

  self.doOtherStuff = function doOtherStuff() {
    console.log('doOtherStuff called!!!')
  }

  self.DoStuff = async function DoStuff() {
    const xhr = self.axiosInstance();
    await xhr.post()
      .then((res) => {
        self.doOtherStuff(res.data);
      })
      .catch((_err) => {
        console.log(_err);
      });
  };
}

module.exports = MyModule;

我的测试如下:

const nock = require('nock');
const sinon = require('sinon');
const MyModule = require('../index.js');
describe('When calling DoStuff succeeds in making the xhr call', () => {
        before(() => {
          nock(apiHostName)
            .post('/some-path')
            .reply(200, { foo: 'bar' });
        });
        it('should call doOtherStuff', async () => {
          const spy = sinon.spy(MyModule, 'doOtherStuff');
          await MyModule.DoStuff();
          sinon.assert.calledOnce(spy);
        });
      });

我在测试运行程序输出中的doOtherStuff函数输出中看到控制台日志,但是测试失败,说间谍被称为零次。

我想知道这是否取决于我正在测试的代码的异步性质,但是我确保在测试中使用async / await。我一定在做些愚蠢的事,我要去哪里错了?

谢谢

更新

因此,我尝试将功能剥离为更基本的功能,现在具有以下功能:

const MyModule = function MyModule() {

  const self = this;

  self.doOtherStuff = function doOtherStuff() {
    console.log('doOtherStuff called!!!')
  }

  self.DoStuff = function DoStuff() {
    self.doOtherStuff();
  };
}

module.exports = MyModule;

因此,这将排除我可能遇到的任何异步/等待问题。

但是即使运行以下简单测试,也不会调用间谍:

const MyModule = require('../index.js');

it('should call doOtherStuff', () => {
  const spy = sinon.spy(MyModule, 'doOtherStuff');
  MyModule.DoStuff();
  sinon.assert.calledOnce(spy);
});

但是,如果我监视console.log,则它会通过。在这里,我一定会误解一个非常基本的原则,但是我不知道它是什么!

这与我的module.exports的声明方式有关吗?因此,即使我试图监视index.jsdoOtherStuff: MyModule.doOtherStuff)中的顶级导出,在测试中对DoStuff进行调用时,这实际上不是内部调用吗?

1 个答案:

答案 0 :(得分:2)

问题

spy中包装的属性不是被调用的属性。

详细信息

sinon.spy接受一个对象和一个属性名称,并将该功能包装在间谍中。

在这种情况下,对象是index.js的模块导出。

模块导出是一个具有两个属性的对象,这些属性指向在MyModule中创建的内部index.js实例上的方法。因此,该对象的doOtherStuff属性现在是spy,而DoStuff属性仍然只是对内部DoStuff实例的MyModule属性的引用。

当测试然后调用MyModule.DoStuff()时,它将调用内部DoStuff实例的MyModule属性,该属性调用内部doOtherStuff实例的MyModule属性记录到控制台。

关键是内部doOtherStuff实例的MyModule属性被直接调用,而doOtherStuff导出的对象的index.js属性从未被调用。 / p>

spy导出的对象的doOtherStuff属性上的index.js然后正确地断言它被调用了0次。

解决方案

确保在实际调用的属性上创建spy

在这种情况下,最简单的方法是直接从MyModule导出index.js实例:

let MyModule = require('./src/index.js');

MyModule = new MyModule();

module.exports = MyModule;

现在,创建spy时,它是直接在内部doOtherStuff实例的MyModule属性上创建的,并且会正确地报告它被调用过一次。