32

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.

Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
Rahul R.
  • 5,965
  • 3
  • 27
  • 38

1 Answers1

62

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

afterEach(function(){
    rootScope.$apply();
});
ndequeker
  • 7,932
  • 7
  • 61
  • 93
Thomas Schultz
  • 2,446
  • 3
  • 25
  • 36
  • 3
    In case you wonder where's that documented: [Differences between Kris Kowal's Q and $q](http://docs.angularjs.org/api/ng/service/$q#differences-between-kris-kowal-s-q-and-q) & [testing](http://docs.angularjs.org/api/ng/service/$q#testing) – the_karel Mar 07 '14 at 10:18
  • 14
    `$apply` usually gets a callback. I prefer `scope.$digest()`, which only digests the current scope, so has better performance. – Frank van Wijk Apr 16 '15 at 10:47
  • afterEach was enough for me. I just tested the promises inside of the afterEach() function and it worked. Ty man – messerbill Jul 09 '15 at 11:18
  • 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. – Gabriel Aug 28 '15 at 08:38
  • Does scope.digest resolve all promises? Even nested promises? Or do I need to call it multiple times to get the code to continue – Edward Jan 12 '16 at 18:16
  • @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. – pmont Mar 08 '16 at 21:51
  • @pmont, you might have to add a code example but I think this might answer your question, http://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. – Thomas Schultz Mar 09 '16 at 15:38
  • 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. – Pat Jul 18 '16 at 18:13