How to resolve $q.all promises in Jasmine unit tests?

26,156

You can try putting $rootScope.$apply() in an afterEach() callback function. Promises resolve on $apply() in Angular.

afterEach(function(){
    rootScope.$apply();
});
Share:
26,156
Rahul R.
Author by

Rahul R.

https://in.linkedin.com/in/rahul-rathaur

Updated on September 23, 2020

Comments

  • Rahul R.
    Rahul R. over 3 years

    My controller has code like below:

    $q.all([qService.getData($scope.id), dService.getData(), qTService.get()])
    .then(function (allData) {
      $scope.data1 = allData[0];
      $scope.data2 = allData[1];
      $scope.data3 = allData[2];
    });
    

    And in my unit tests i am doing something like this:

    beforeEach(inject(function($rootScope, $q, $location){// and other dependencies... 
      qServiceSpy = spyOn(_qService, 'getData').andCallFake(function () {
        var data1 = {
          id: 1,
          sellingProperty: 1,
        };
        var d = $q.defer();
        d.resolve(data1);
        return d.promise;
      });
    
      dServiceSpy = spyOn(_dService, 'getData').andCallFake(function () {
        var data2 = [{ "id": "1", "anotherProp": 123 }];
        var d = $q.defer();
        d.resolve(data2);
        return d.promise;
      });
      qTServiceSpy = spyOn(_qTService, 'get').andCallFake(function () {
        var data3 = [{ id: 0, name: 'Rahul' }];
        var d = $q.defer();
        d.resolve(data3);
        return d.promise;
      });
      rootScope = $rootScope;
    });
    

    Now in my test i am checking if services are called and the data1, data2 are not undefined..

    it('check if qService' got called, function() {
      expect(scope.data1).toBeUndefined();
      rootScope.$digest();
      expect(_quoteService.getQuote).toHaveBeenCalled();
    });
    it('check if "data1" is defined', function () {
      expect(scope.data1).toBeUndefined();
      rootScope.$digest();
      expect(scope.data1).toBeDefined();
    });
    

    my problem is, this was working fine until i replaced my individual service calls in controller with q.all and in tests scope.$apply with rootScope.$digest. With q.all and rootScope.$digest (tried using scope.$apply as well) both tests fails with error:

    10 $digest() iterations reached. Aborting!

    if i remove rootScope.$digest then the promises never get resolves and tests fails saying

    expected undefined to be defined.

    Any help how should i unit tests code with q.all?

    came across this post

    But that also doesn't help as i am already trying to use $digest.

  • the_karel
    the_karel about 10 years
    In case you wonder where's that documented: Differences between Kris Kowal's Q and $q & testing
  • Frank van Wijk
    Frank van Wijk about 9 years
    $apply usually gets a callback. I prefer scope.$digest(), which only digests the current scope, so has better performance.
  • messerbill
    messerbill almost 9 years
    afterEach was enough for me. I just tested the promises inside of the afterEach() function and it worked. Ty man
  • Gabriel
    Gabriel over 8 years
    I personally experience a wired behavior with this. I had 3 promises. Before adding afterEach...rootScope.$apply() no one works. After only one of them works.
  • Edward
    Edward over 8 years
    Does scope.digest resolve all promises? Even nested promises? Or do I need to call it multiple times to get the code to continue
  • pmont
    pmont about 8 years
    @Edward - based on my experience, this doesn't resolve nested promises. I'd be grateful if someone would explain how to do so. In my case I'm using $q.all(). The first level of promises is resolved by $digest (or $apply), but the final success handler for the group of promises is never triggered.
  • Thomas Schultz
    Thomas Schultz about 8 years
    @pmont, you might have to add a code example but I think this might answer your question, stackoverflow.com/a/26224595/89702. You can pass in a list of the promises that you want to wait for and then run $apply at the end once everything is resolved.
  • Pat
    Pat almost 8 years
    I was testing with jasmine v1 and had forgotten to enclose the code in a runs function (I also added a waits(1); just prior) in order to run async. Once I did that, $rootScope.$digest(); worked as expected.