Stub 一个 ES6 EventEmitter class 函数

Stub a ES6 EventEmitter class function

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

依赖关系Class

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 来创建一个存根,但我无法对 class 有效发出事件的函数进行存根。我根据 [Feature request] stub emits 对我的测试进行了建模,但不得不进行一些修改,因为被存根的事件发射器依赖项是 class.

测试

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...
  });
});

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

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

然后跳转到Dependent Module Under Test中的第5行

然后在 Dependent Module Under Test 的第 19 行,它调用 fooBar.doSomething()

然后它跳转到 Dependency Class 中的第 12 行,那里 this.doingSomething 是 false,所以它发出 startedDoingSomething.

然后跳转到Dependent Module Under Test中的第7行,调用fooBar.removeAllListeners();,也就是说同一个文件第34行注册的事件处理器也被移除

这是真的吗?

现在假设第 7 行中的 fooBar.removeAllListeners Dependent Module Under Test 被注释掉了。

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

问题是调用FooBar.prototype.emit时,context中的this不等于Dependent Module Under Test中第2行声明的fooBar。 (等于FooBar.prototype

因此FooBar.prototype.emit('alreadyDoingSomething');不会触发在Dependent Module Under Test中第12行定义的事件处理程序。

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

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

现在假设我们将 exports.fooBar = fooBar; 添加到 Dependent Module Under Test 的末尾。

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

现在在测试中,当调用 dependentModule.fooBar.pause() 时,会触发 alreadyDoingSomething。

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