首页 > 解决方案 > 即使正在调用监视的函数,Sinon spy 也会失败

问题描述

我无法理解为什么 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.js的 ( doOtherStuff: MyModule.doOtherStuff) 中监视顶级导出,这不是在我的测试中调用 DoStuff 时在内部实际调用的内容?

标签: node.jsmocha.jssinonchai

解决方案


问题

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

细节

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

在这种情况下,对象是index.js.

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

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

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

然后导出的对象的spyon属性正确断言它被调用了 0 次。doOtherStuffindex.js

解决方案

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

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

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

MyModule = new MyModule();

module.exports = MyModule;

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


推荐阅读