【问题标题】:Sinon spy fails even though function spied on is being called即使正在调用监视的函数,Sinon spy 也会失败
【发布时间】:2019-01-08 15:06:13
【问题描述】:

我无法理解为什么 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 时在内部实际调用的内容?

【问题讨论】:

  • 看起来你在监视ALClient,而不是MyModule
  • 是的,对不起,当我把这段代码 sn-p 时,这是一个错字。我已经纠正了这个,但原来的问题仍然存在。谢谢
  • 您的意思是先创建一个 MyModule 的实例吗?在您的示例中,MyModule 没有 DoStuff 属性,也没有您可以监视的 doOtherStuff 属性。使用您显示的代码,您应该看到由 Sinon 抛出的“尝试包装未定义的属性 doOtherStuff”异常:jsfiddle.net/j7ksobce/1。但是,如果您创建 MyModule 的实例,它可以正常工作:jsfiddle.net/qm0gj65o
  • 我在测试规范文件开头的模块中使用require,因此这些属性在测试运行时可用。我没有在我的测试规范 sn-p 中显示 require 语句,我现在添加了它。我怀疑如果您遵循module.exports 语法,它可能会开始为您失败。
  • @mindparse 我看到断开连接的位置。在您的第一段示例代码中,您在分配module.exports = MyModule; 之前执行MyModule = new MyModule();。在您的最后一个示例中,您从不调用new MyModule()。你的两个例子根本不同。

标签: node.js mocha.js sinon chai


【解决方案1】:

问题

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

详情

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

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

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

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

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

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

解决方案

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

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

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

MyModule = new MyModule();

module.exports = MyModule;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-13
    • 1970-01-01
    • 2018-01-25
    • 2017-02-12
    • 2016-01-02
    • 2018-02-23
    • 2017-04-21
    • 1970-01-01
    相关资源
    最近更新 更多