揭示用于测试的模块替代或变体

Revealing Module Alternative or Variation for Testing

概览

我已经使用 Revealing Module Pattern 几个月了,我正在寻找此模式的替代方案或变体,以解决我目前遇到的两个问题具有事件处理程序和可测试性。我知道我可以结合下面的内容来解决我的问题,但我希望找到一个干净的替代方案,我可以始终如一地使用它来解决我当前的两个问题。

揭示模块模式

所以在这个例子中,我对事件处理程序没有问题,但我不能模拟函数调用 在函数内进行隔离测试:

var Lion = (function () {

  // Reference to rawr works as expected when the click event is triggered
  function watch() {
    document.addEventListener('click', rawr);
  }

  function rawr() {
    console.log('rawr');
  }

  function attack() {
    console.log('attack');
  }

  /*
   * Can't test goCrazy() in isolation.  Mocking rawr() and attack()
   * has no effect as demonstrated below.
   */
  function goCrazy() {
    rawr();
    attack();

    // Important "crazy" logic
  }

  return {
    watch: watch,
    rawr: rawr,
    attack: attack,
    goCrazy: goCrazy
  };
}());

module.exports = Lion;

示例测试用例 (Jasmine)

describe('Mock Check', function () {

  it('should mock rawr() and attack() and test only goCrazy logic', function () {
    var lion = require('Lion');

    spyOn(lion, 'rawr').and.reutrnValue(true);  
    spyOn(lion, 'attack').and.reutrnValue(true);  

    var crazy = lion.goCrazy();

    expect(lion.rawr).toHaveBeenCalled(); // <-- Fails
    expect(lion.attack).toHaveBeenCalled(); // <-- Fails

    // can't test goCrazy() logic in isolation :(
  });
});

同一模块使用 this 代替并使用 new

调用

在这个例子中,我可以成功地在函数内模拟调用函数,但是如果我尝试添加一个事件处理程序,当事件被触发时 this 变成 undefined

var Lion = function () {

  // Reference to 'this' becomes undefined when event is triggered
  this.watch = function () {
    document.addEventListener('click', this.rawr);
  }

  this.rawr = function () {
    console.log('rawr');
  }

  this.attack = function () {
    console.log('attack');
  }

  /*
   * Can successfully test goCrazy() in isolation by being able to mock 
   * rawr() and attack() as needed
   */
  this.goCrazy = function () {
    this.rawr();
    this.attack();

    // Important "crazy" logic
  }
};

module.exports = Lion;

示例测试用例 (Jasmine)

describe('Mock Check', function () {

  it('should mock rawr() and attack() and test only goCrazy logic', function () {
    var Lion = require('Lion');
    var lion = new Lion();

    spyOn(lion, 'rawr').and.reutrnValue(true);  
    spyOn(lion, 'attack').and.reutrnValue(true);  

    var crazy = lion.goCrazy();

    expect(lion.rawr).toHaveBeenCalled(); // <-- Success
    expect(lion.attack).toHaveBeenCalled(); // <-- Success

    // testing goCrazy logic in isolation :)
  });
});

感谢您的宝贵时间。如果需要任何说明,请告诉我,我会修改我的 post.

这里的实际问题是,事件处理程序丢失了当前对象的上下文。你可以这样绑定

document.addEventListener('click', this.rawr.bind(this));

这将确保每当调用 rawr 时,rawr 中的 this 对应于您创建的 lion 对象。