Jagrut
Jagrut

Reputation: 922

$httpBackend.flush() method throws Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting

I am trying to unit test my AngularJS application using Karma and Jasmine. I want to mock the $http service. For that, I am using the $httpBackend method. Below is my service that I want to test:

angular.module('MyModule').factory('MyService', function($http, $log, $parse, $q, $timeout, $filter, MyOtherService1, MyOtherService2){
var service = {};
   service.getSomething = function(id){
     return $http.get('/somePath/subpath/' + id);
   }
});

My unit test for this service is:

describe("myTest", function(){
    var myService, $httpBackend, scope, mockMyOtherService1, mockMyOtherService2;

    var myResponse =
    {
        foo:'bar'
    };

    beforeEach(module("MyModule"));

    beforeEach(inject(function(_MyService_, $injector){

        $httpBackend = $injector.get('$httpBackend');
        myService = _MyService_;
        scope = $injector.get('$rootScope').$new();
        mockMyOtherService1 = $injector.get('MyOtherService1');
        mockMyOtherService2 = $injector.get('MyOtherService2');

    }));

    beforeEach(function(){
        //To bypass dependent requests
        $httpBackend.whenGET(/\.html$/).respond(200,'');
    });

    //If I uncomment the below afterEach block, the same error is shown at next line.
    /*afterEach(function() {
         $httpBackend.verifyNoOutstandingExpectation();
         $httpBackend.verifyNoOutstandingRequest();
     });*/

    //This test passes successfully
    it("should check if service is instantiated", function () {
        expect(myService).toBeDefined();
    });

    //This test passes successfully
    it("should expect dependencies to be instantiated", function(){
        expect($httpBackend).toBeDefined();
    });

    //The problem is in this test
    it("should get the getSomething with the provided ID", function() {
        $httpBackend.whenGET('/somePath/subpath/my_123').respond(200,myResponse);            
        var deferredResponse = myService.getSomething('my_123');

        //The error is shown in next line.
        $httpBackend.flush();      

        //If I comment the $httpBackend.flush(), in the next line, the $$state in deferredResponse shows that the Object that I responded with is not set i.e. it does not matches the 'myResponse'.
        expect(deferredResponse).toEqual(myResponse);

    });
});

This is an emergency problem and I need help regarding the same as soon as possible. I will be very grateful for your answer.

Upvotes: 7

Views: 874

Answers (3)

oooyaya
oooyaya

Reputation: 1803

I've recently had this problem when updating a project from Angular 1.2 to 1.4. The test code looked something like:

it('should call /something', function(){
    httpBackend.expectGET('/something').respond(200);
    scope.doSomething();
    httpBackend.flush();
});

The error was the infdig past 10 iterations. It was caused by invoking the .flush() method. I figured out this is seemingly because there were no pending promises created within doSomething().

Once I added a promise somewhere within doSomething() or inner methods, the infdig problem went away.

I suspect - and this is 100% speculation so don't let it influence your development - this is because httpBackend does some trickery to wait for promises, which maybe involves digesting repeatedly until there's a change. Since there's no promises, there's no changes - infinite digest.

Upvotes: 1

Jagrut
Jagrut

Reputation: 922

The problem was I needed to inject $location in my spec files even though they are not injected in the services. After injection, all worked well! Hope this helps someone who gets stuck in the same situation.

Upvotes: 2

ogugger
ogugger

Reputation: 122

You will get a promise from your service. So change your test code to:

//The problem is in this test
it("should get the getSomething with the provided ID", function (done) {
  $httpBackend.expectGET('/somePath/subpath/my_123').respond(200,myResponse);
  var deferredResponse = myService.getSomething('my_123');

  deferredResponse.then(function (value) {
    expect(value.data).toEqual(myResponse);
  }).finally(done);

  $httpBackend.flush();
});

Upvotes: 1

Related Questions