在jasmine中断言绑定函数调用

时间:2016-12-17 08:30:54

标签: angularjs unit-testing jasmine

我创建了以下角度工厂名为" juicefactory"它返回以下内容:

  return {
       aj: pathbuilder.build.bind(this, 'apple', 'juice'),
       oj: pathbuilder.build.bind(this, 'orange', 'juice'), 
       mj: pathbuilder.build.bind(this, 'mango', 'juice')
  };

并使用茉莉花编写以下单元测试用例。

before(function(){
    mockpathbuilder = {
        build: function(fruit, type) {
                  return fruit & ' ' & type;
               }
         };

 module(function ($provide) {
      $provide.value('pathbuilder', mockpathbuilder);
  });
});

 it('should return value from mock dependency', function () {

     spyon(mockpathbuilder, 'build');

     juicefactory['aj']();

     expect(mockpathbuilder.build).toHaveBeenCalledWith('apple', 'juice');
});

通过代码调试时,它使用两个参数调用模拟的构建方法,但由于某种原因断言失败。能帮助你解决上面代码中缺少的内容吗?

1 个答案:

答案 0 :(得分:1)

这可能是一个棘手的问题。

说明

首先,了解bind返回一个新函数,该函数使用强制上下文包装对原始函数的引用副本。

这就是为什么即使您稍后重新定义原始函数,绑定函数也会以相同的方式工作:

var pathbuilder = {
  build: function() {
    console.log('Original');
  }
};

var juicefactory = {
  a: pathbuilder.build.bind(this)
};

pathbuilder.build(); // Logs: Original

juicefactory.a(); // Logs: Original

pathbuilder.build = function() {
  console.log('New');
};

pathbuilder.build(); // Logs: New

juicefactory.a(); // Still logs: Original

您的案例中的问题是spyOn会将新函数分配给pathbuiler.build,这是一个新的包装函数,其中包含对原始函数的引用。

这意味着对pathbuilder.build的所有后续调用都将触及由spyOn创建的新包装函数,调用将被注册,然后将调用原始函数(如果and.callThrough()是在创建间谍时使用。)

但是,使用juicefactory.a创建的对bind的调用将触及原始pathbuilder.build函数,而不是spyOn创建的包装器。这意味着不会记录通话。

解决方案

一种解决方案是将spyOn(mockpathbuilder, 'build')移出it块,然后移动到创建模块的before

这基本上意味着当在测试环境中创建juicefactory并执行pathbuilder.build.bind时,pathbuilder.build将成为spyOn创建的包装函数,一切都会工作

另一个解决方案是将pathbuilder.build.bind的执行包装在一个函数中:

aj: function() {
  pathbuilder.build.bind(this, 'apple', 'juice')();
}

这意味着每次执行aj函数时都会在运行时完成绑定。但是,除非这是您真正想要的功能,否则我不建议这样做。它的性能也更差(尽管很可能是微不足道的),因为绑定可能会发生多次。