Question

I want to be thorough, so please bear with me, there's going to be a lot here. We have a remote logging service function that will send us some client-side information when we want to. Something like this:

callHome: function(message){
    var deferred, promise;
    try{
        if (someService.getRemoteLoggingEnabled())
        {
            //collect all the info into remoteLog
            promise = $http.post("Logging", remoteLog);
            wipeLog();
        }
        else
        {
            deferred = $q.defer();
            promise = deferred.promise;
            deferred.resolve();
        }
    }
    catch(error)
    {
        try{
            if (!promise)
            {
                deferred = $q.defer();
                promise = deferred.promise;
            }
            deferred.reject(error.message);
        }
        catch(e2){}
    }
    return promise;
}

This all works just fine when running it in the actual app. The problem comes when trying to write unit tests for it. I have tests for when remote logging isn't enabled and for when there is an error. Those look like this:

it ("should resolve the promise with nothing when remote logging is turned off", inject(function($rootScope) {
    remoteLoggingEnabled = false; //this is declared above a beforeEach that mocks getRemoteLoggingEnabled
    var successSpy = jasmine.createSpy("success");
    var failSpy = jasmine.createSpy("fail");
    var promise = loggingService.callHome("Hello World");
    promise.then(successSpy, failSpy);
    $rootScope.$digest();

    expect(successSpy).toHaveBeenCalledWith(undefined);
    expect(failSpy).not.toHaveBeenCalled();
}));
it ("should reject the promise when there is an error with the error message", inject(function($rootScope) {
    remoteLoggingEnabled = true;
    var successSpy = jasmine.createSpy("success");
    var failSpy = jasmine.createSpy("fail");
    //angular.toJson is called while it's gathering client-side info
    spyOn(angular, "toJson").andCallFake(function() {throw new Error("This is an error");}); 
    var promise = loggingService.callHome("Hello World");
    promise.then(successSpy, failSpy);
    $rootScope.$digest();

    expect(successSpy).not.toHaveBeenCalled();
    expect(failSpy).toHaveBeenCalledWith("This is an error");
}));

These work great. I next wanted to add tests for when it actually made the makes the request. I put together a test like this:

it ("should resolve the promise with the http info when it makes a successful request", inject(function($rootScope, $httpBackend) {
    remoteLoggingEnabled = true;
    var successSpy = jasmine.createSpy("success");
    var failSpy = jasmine.createSpy("fail");
    $httpBackend.expect("POST", new RegExp("Logging"), function(jsonStr){
        //not concerned about the actual payload
        return true;
    }).respond(200);
    var promise = loggingService.callHome("Hello World");
    promise.then(successSpy, failSpy);
    $httpBackend.flush();
    $rootScope.$digest();

    expect(successSpy).toHaveBeenCalledWith(/*http info*/);
    expect(failSpy).not.toHaveBeenCalled();
}));

However, this test just hangs. I stepped through the code and it gets stuck in the $rootScope.$digest() call of $httpBackend.flush(), specifically in this while loop:

      while(asyncQueue.length) {
        try {
          asyncTask = asyncQueue.shift();
          asyncTask.scope.$eval(asyncTask.expression);
        } catch (e) {
          clearPhase();
          $exceptionHandler(e);
        }
        lastDirtyWatch = null;
      }

I've inspected the asyncTask.expression as it loops through, but I can't find any pattern to what it's doing.

I'm still getting a grasp on promises and how to use them, so I hope there's just something fundamentally wrong I'm doing here. Any help would be much appreciated.

Était-ce utile?

La solution

The problem was just in the setup of my test (not shown as part of the question). This callHome function gets called anytime there is an Error via a decorated $exceptionHandler. There was an error during the test on callHome, so it got called again, and then just looped from there. I fixed that error, and now it all works just fine.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top