How to unit test an angularjs promise chain using $httpBackend

邮差的信 提交于 2019-12-03 05:59:16

I'm a beginner in testing Angular, but I've setup a plnkr that tests a very similar setup to yours with a sucessfull "second" then/promise call

http://plnkr.co/edit/kcgWTsawJ36gFzD3CbcW?p=preview

The below code snippets are slightly simplified versions of the above plnkr.

The key points I've found are

  • I note the function traverseTheStuff doesn't call $http/$httpBackend at all. It only uses functions defined in $q promises, so the testing on assumes use of $q, and injects that

    var deferred1 = null;
    var deferred2 = null;
    var $q = null;
    
    beforeEach(function() {
      inject(function(_$q_) {
        $q = _$q_;
      });
    });
    
    beforeEach(function() {
      deferred1 = $q.defer();
      deferred2 = $q.defer();
    }
    
  • The functions to be called asynchronously are spied/stubbed with their promise return values, where the promise is created in the test itself, so their actual implementation isn't called when testing traverseTheStuff

    spyOn(MyService,'traverseTheStuff').andCallThrough();
    spyOn(MyService,'getRootData').andReturn(deferred1.promise);
    spyOn(MyService,'getNextLevel').andReturn(deferred2.promise);
    spyOn(MyService,'aggregateTheStuff');
    
  • There aren't any calls to "then" in the test, only to "resolve" on the promises created in the test, followed by $rootScope.$apply(), to then actually call the "then" callbacks in traverseTheStuff, which we can also test are called

    beforeEach(function() {
      spyOn(deferred1.promise, 'then').andCallThrough();
    });
    
    beforeEach(function() {
      deferred1.resolve(testData);
      $rootScope.$apply(); // Forces $q.promise then callbacks to be called
    });
    
    it('should call the then function of the promise1', function () { 
      expect(deferred1.promise.then).toHaveBeenCalled();
    });
    
  • Each promise must be resolved/$apply-ed to call the next "then" function in the chain. So. to get the test to call aggregateTheStuff (or rather, its stub), the second promise, returned from the getNextLevel stub, must also be resolved:

    beforeEach(function() {
      deferred2.resolve(testLevel);
      $rootScope.$apply(); // Forces $q.promise then callbacks to be called
    });
    
    it('should call aggregateTheStuff with ' + testLevel, function () {
      expect(MyService.aggregateTheStuff).toHaveBeenCalledWith(testLevel);
    });
    

An issue with all of the above, is that it assumes certain behaviour from $q and $rootScope. I was under the understanding unit tests like this shouldn't make this assumptions, in order to truly only test one bit of code. I've not worked out how to get around this, or if I'm misunderstanding.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!