最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Testing a debounced function in AngularJS with Jasmine never calls the function - Stack Overflow

programmeradmin3浏览0评论

I have a method in a service that uses underscore's debounce.

Inside that method is a call to a method on a different service. I'm trying to test that the different service is called.

In my attempts to test the debounced method, the different services' method is never called, and jasmine fails with:

"Expected spy aMethod to have been called."

I know for a fact that it IS called (it logs to console in chrome), it's just called AFTER the expectation already failed.

So... (preferably) without adding Sinon or another dependency and with
bonus points* given to a solution doesn't have to turn the _.debounce into a $timeout...

How do?

angular.module('derp', [])
.service('herp', function(){ 
   return {
     aMethod: function(){ 
       console.log('called!'); 
       return 'blown'; 
     }
   }; 
 })
 .service('Whoa', ['herp', function(herp){
   function Whoa(){
     var that = this;
     this.mindStatus = 'meh';
     this.getMind = _.debounce(function(){
       that.mindStatus = herp.aMethod();
     }, 300);
   }
   return Whoa;
 }]);

tests:

describe('Whoa', function(){
  var $injector, whoa, herp;

  beforeEach(function(){
    module('derp');
    inject(function(_$injector_){
      var Whoa;
      $injector = _$injector_;
      Whoa = $injector.get('Whoa');
      herp = $injector.get('herp');
      whoa = new Whoa();
    });
  });

  beforeEach(function(){
    spyOn(herp, 'aMethod').andCallThrough();
  });

  it('has a method getMind, that calls herp.aMethod', function(){
    whoa.getMind();
    expect(herp.aMethod).toHaveBeenCalled();
  });
});

Why have the AngularJS Testing gods forsaken me?

* I do not know how to give actual bonus points on stackoverflow, but if it is possible, i will.

I have a method in a service that uses underscore's debounce.

Inside that method is a call to a method on a different service. I'm trying to test that the different service is called.

In my attempts to test the debounced method, the different services' method is never called, and jasmine fails with:

"Expected spy aMethod to have been called."

I know for a fact that it IS called (it logs to console in chrome), it's just called AFTER the expectation already failed.

So... (preferably) without adding Sinon or another dependency and with
bonus points* given to a solution doesn't have to turn the _.debounce into a $timeout...

How do?

angular.module('derp', [])
.service('herp', function(){ 
   return {
     aMethod: function(){ 
       console.log('called!'); 
       return 'blown'; 
     }
   }; 
 })
 .service('Whoa', ['herp', function(herp){
   function Whoa(){
     var that = this;
     this.mindStatus = 'meh';
     this.getMind = _.debounce(function(){
       that.mindStatus = herp.aMethod();
     }, 300);
   }
   return Whoa;
 }]);

tests:

describe('Whoa', function(){
  var $injector, whoa, herp;

  beforeEach(function(){
    module('derp');
    inject(function(_$injector_){
      var Whoa;
      $injector = _$injector_;
      Whoa = $injector.get('Whoa');
      herp = $injector.get('herp');
      whoa = new Whoa();
    });
  });

  beforeEach(function(){
    spyOn(herp, 'aMethod').andCallThrough();
  });

  it('has a method getMind, that calls herp.aMethod', function(){
    whoa.getMind();
    expect(herp.aMethod).toHaveBeenCalled();
  });
});

Why have the AngularJS Testing gods forsaken me?

* I do not know how to give actual bonus points on stackoverflow, but if it is possible, i will.

Share Improve this question edited May 16, 2018 at 18:31 mdewitt 2,53420 silver badges23 bronze badges asked Dec 28, 2015 at 22:43 Andrew LuhringAndrew Luhring 1,8941 gold badge18 silver badges37 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 15

You just need to mock lodash debounce method:

describe('Whoa', function(){
  var $injector, whoa, herp;

  beforeEach(function(){
    module('derp');
    spyOn(_, 'debounce').and.callFake(function(cb) { return function() { cb(); } });
    inject(function(_$injector_){
      var Whoa;
      $injector = _$injector_;
      Whoa = $injector.get('Whoa');
      herp = $injector.get('herp');
      whoa = new Whoa();
    });
  });

  beforeEach(function(){
    spyOn(herp, 'aMethod').andCallThrough();
  });

  it('has a method getMind, that calls herp.aMethod', function(){
    whoa.getMind();
    expect(herp.aMethod).toHaveBeenCalled();
  });
});

Angular $timeout has advantage in tests because it is mocked in tests to be synchronous. The one won't have this advantage when third-party asynchronous tools one used. In general asynchronous specs will look like that:

var maxDelay = 500;

  ...
  it('has a method getMind, that calls herp.aMethod', function (done){
    whoa.getMind();
    setTimeout(function () {
      expect(herp.aMethod).toHaveBeenCalled();
      done();
    }, maxDelay);
  });

Since Underscore debounce doesn't offer flush functionality (while the recent version of Lodash debounce does), asynchronous testing is the best option available.

My debounced function took arguments so I mocked _.debounce like this

spyOn(_, 'debounce').and.callFake(function(cb) {return cb});

(slight modification on @Wawy's answer)

发布评论

评论列表(0)

  1. 暂无评论