Javascript 存根ES6 EventEmitter类函数

Javascript 存根ES6 EventEmitter类函数,javascript,node.js,unit-testing,sinon,stubbing,Javascript,Node.js,Unit Testing,Sinon,Stubbing,我有一个扩展EventEmitter的依赖项类,我需要测试使用此依赖项的函数如何根据它触发的事件做出反应。如何存根EventEmitter类的函数 依赖类 const EventEmitter = require('events'); class FooBar extends EventEmitter { constructor() { super(); this.doingSomething = false; } doSomething() { if

我有一个扩展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
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...
  });
});
我使用Sinon来创建存根,但我无法有效地存根发出事件的类函数。我对的测试进行了建模,但由于要存根的事件发射器依赖项是一个类,因此必须进行一些修改

测试

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
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()

然后跳转到被测从属模块中的第5行

然后在被测依赖模块的第19行中,它调用
fooBar.doSomething()

然后它跳转到依赖类的第12行,这里有
这个。doingSomething
是false,所以它发出
startedDoingSomething

然后它跳转到被测依赖模块的第7行,调用
fooBar.removeAllListeners(),这意味着在同一文件的第34行注册的事件处理程序也将被删除

这是真迹吗

现在假设第7行中的
fooBar.removeAllListeners
被测试的依赖模块被注释掉

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

问题是,当调用
FooBar.prototype.emit
时,上下文中的
不等于测试中的依赖模块第2行中声明的
FooBar
。(它等于
FooBar.prototype

所以
FooBar.prototype.emit('alreadyDoingSomething')不会触发测试中的依赖模块第12行中定义的事件处理程序

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

但这是不可能的,因为永远不会导出
fooBar
,除非使用类似的库

现在假设我们添加
exports.fooBar=fooBar至受测从属模块的末尾

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

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

现在您应该看到
已经在控制台中执行了一些操作。

是-第7行的removeAllListeners()调用是有意的,因为它可以防止myFunction在发出单个事件后挂起。在这种特殊情况下,我只关心发出的第一个事件,但需要处理所有情况。您建议的导出修复确实有效——即使没有对removeAllListeners()调用进行注释——但不幸的是,仅使用构造函数而不是类的确切实例无法实现这一点。