存根ES6 EventEmitter类函数

时间:2017-02-18 04:56:31

标签: javascript node.js unit-testing sinon stubbing

我有一个扩展EventEmitter的依赖类,我需要测试使用此依赖项的函数将如何根据它触发的事件做出反应。你如何存根EventEmitter类的函数?

依赖类

const EventEmitter = require('events');

class FooBar extends EventEmitter {

  constructor() {
    super();

    this.doingSomething = false;
  }

  doSomething() {
    if (this.doingSomething === false) {
      this.doingSomething = true;
      this.emit('startedDoingSomething');
    }
    else {
      this.emit('alreadyDoingSomething');
    }
  }
}

module.exports = FooBar;

受测试的相关模块

let Foobar = require('FooBar');
let fooBar = new FooBar();

exports.myFunction = () => {
  // Set up listeners
  fooBar.once('startedDoingSomething', () => {
    fooBar.removeAllListeners();
    // Some functionality
    console.log('Started Doing Something');
  });

  fooBar.once('alreadyDoingSomething', () => {
    fooBar.removeAllListeners();
    // Some functionality
    console.log('Already Doing Something');
  });

  // Call the event-emitting function
  fooBar.doSomething();
};

// Other functions that use fooBar

我使用Sinon来创建一个存根,但我还没有能够存储有效发出事件的类函数。我将我的测试建模为[Feature request] stub emits,但由于事件发射器依赖关系是一个类,因此必须进行一些修改。

测试

let chai = require('chai');
let sinon = require('sinon');
let FooBar = require('FooBar');
let dependentModule = require('./dependentModule');

describe('Dependent Module', () => {
  it('alreadyDoingSomething', () => {
    sinon.stub(FooBar.prototype, 'pause', () => {
      FooBar.prototype.emit('alreadyDoingSomething');
    });

    // Assertion statements here
    expect(dependentModule.myFunction()).to...
  });
});

即使正在调用存根函数,此方法实际上也不会发出事件。

1 个答案:

答案 0 :(得分:0)

测试中的第13行,它调用dependentModule.myFunction()

然后它跳转到受测试的相关模块中的第5行

然后在受测试的从属模块的第19行中,它调用fooBar.doSomething()

然后它跳转到依赖类中的第12行,this.doingSomething为假,因此它会发出startedDoingSomething

然后它跳转到Dependent Module Under Test中的第7行,它调用fooBar.removeAllListeners();,这意味着在同一文件的第34行注册的事件处理程序也被删除。

这是有意的吗?

现在假设第7行中的fooBar.removeAllListeners注释掉了受管模块。

在测试中的第13行之后,它会调用FooBar.prototype.pause,然后调用FooBar.prototype.emit('alreadyDoingSomething');

问题是,当调用FooBar.prototype.emit时,上下文中的this不等于“受测试的相关模块”中第2行中声明的fooBar。 (它等于FooBar.prototype

因此FooBar.prototype.emit('alreadyDoingSomething');不会触发在受测试的从属模块中第12行定义的事件处理程序。

我们需要找到一种方法来调用fooBar.emit('alreadyDoingSomething')

但这是不可能的,因为永远不会导出fooBar,除非使用了rewire这样的库。

现在假设我们将exports.fooBar = fooBar;添加到受测试的从属模块的末尾。

我们还将Test中的第9行更改为this.emit('alreadyDoingSomething')这很重要,因为我们需要在调用emit时需要作为实例的上下文,在我们的例子fooBar中。

现在在Test中,当调用dependentModule.fooBar.pause()时,会触发已经做过的东西。

现在您应该在控制台中看到Already Doing Something