Angularjs 角度单元试验.相同服务中的模拟方法/闭包

Angularjs 角度单元试验.相同服务中的模拟方法/闭包,angularjs,unit-testing,sinon,Angularjs,Unit Testing,Sinon,我目前正在尝试使用单元测试我的angular service,但遇到了一个问题,希望有人能够解释为什么会发生这种情况。我重建了我目前的项目,以说明手头的问题 我还提供了一份 我有一项服务,peopleService: (function (){ angular.module('myApp') .factory('peopleService', peopleService); peopleService.$inject = ['$q']; function peo

我目前正在尝试使用单元测试我的angular service,但遇到了一个问题,希望有人能够解释为什么会发生这种情况。我重建了我目前的项目,以说明手头的问题

我还提供了一份

我有一项服务,
peopleService

(function (){

  angular.module('myApp')
    .factory('peopleService', peopleService);

    peopleService.$inject = ['$q'];

    function peopleService ($q){
      var people = ['Homer', 'Marge', 'Bart', 'Lisa', 'Maggie'];

      // in actual project, this makes an http request
      function getFamily () {
         return people;
      }

      function getAdults (){
        var family = getFamily();
        return family.filter(function (person){
          return person === 'Homer' || person === 'Marge';
        });
      }

      return {
        getFamily: getFamily,
        getAdults: getAdults
      }
    }

}());
在这个服务中,我的方法使用
getFamily
,过滤结果并返回数据

在我的单元测试中,我试图模拟
getFamily
,看看是否调用了该方法。这就是问题所在

我尝试的第一件事是删除该方法并覆盖当前方法,如下所示:

beforeEach(function (){
  module('myApp');

  inject(function (_peopleService_){
    peopleService = _peopleService_; // get the service
    sinon.stub(peopleService, 'getFamily'); // stub it
  });
});
然后,我去测试
get成人
是否调用
getFamily
方法:

it('getAdults should call "getFamily" once', function(){
     peopleService.getAdults();
     expect(peopleService.getFamily.calledOnce).toBe(true);
});
测试失败,未调用存根方法

我调试并发现,尽管函数实际上已更改:

服务仍然保留对创建服务时使用的方法的引用(闭包):

我最初的想法是我没有正确地使用这个方法。然后,我尝试使用(
$provide.value
)覆盖该方法,最后得到了相同的结果(闭包保留在原始方法上)

解决方法是使用

 function getAdults (){
    var family = this.getFamily(); // <-- by using this.getFamily would reference the mock
    return family.filter(function (person){
      return person === 'Homer' || person === 'Marge';
    });
  }
函数{
var family=this.getFamily();//在对象上存根方法时,将覆盖该对象的属性,而不是它引用的原始函数

以以下代码为例:

function myFunction () {};
var myObj = { prop: myFunction };

myObj.prop === myFunction; // true
myObj.prop = 'changed';

typeof myFunction === 'function'; // true
myObj.prop === myFunction; // false
更改
myObj.prop
并没有更改原始函数,
myFunction
本身仍然存在。
myObj.prop
但是,它失去了对
myFunction
的引用。如果这是在sinon世界中,stubing只是将
myObj.prop
的引用更改为存根对象

这就是为什么在调用同一服务中的另一个函数的服务中测试代码时,该代码需要引用该服务返回的同一对象。如果要避免在任何地方使用
This
关键字,可以这样构造服务:

angular.module('myApp')
  .factory('peopleService', peopleService);

peopleService.$inject = ['$q'];

function peopleService ($q){
  var service = {
    getFamily: getFamily,
    getAdults: getAdults
  };

  return service;

  function getFamily () {
     // ...
  }

  function getAdults (){
    var family = service.getFamily(); // <-- reference service.getFamily()
    // ...
  }
}
angular.module('myApp')
.工厂(“peopleService”,peopleService);
peopleService.$inject=['$q'];
功能peopleService($q){
var服务={
getFamily:getFamily,
Get成人:Get成人
};
回程服务;
函数getFamily(){
// ...
}
函数(0){

var family=service.getFamily();//看来您的plunker已经不可用了。我认为这个答案应该得到奖励,它非常清晰和完整