Angularjs promise not being resolved in unit test
angularjs, jasmine, javascript, unit-testing
Solution
I guess that the key to this "mystery" is the fact that AngularJS will automatically resolve promises (and render results) if those used in an interpolation directive in a template. What I mean is that given this controller:
MyCtrl = function($scope, $http) {
$scope.promise = $http.get('myurl', {..});
}
and the template:
<span>{{promise}}</span>
AngularJS, upon $http call completion, will "see" that a promise was resolved and will re-render template with the resolved results. This is what is vaguely mentioned in the $q documentation:
$q promises are recognized by the templating engine in angular, which means that in templates you can treat promises attached to a scope as if they were the resulting values.
The code where this magic happens can be seen here.
BUT, this "magic" happens only when there is a template (`$parse` service, to be more precise) at play. In your unit test there is no template involved so promise resolution is not propagated automatically.
Now, I must say that this automatic resolution / result propagation is very convenient but might be confusing, as we can see from this question. This is why I prefer to explicitly propagate resolution results as you did:
var MyController = function($scope, service) {
service.getStuff().then(function(result) {
$scope.myVar = result;
});
}
Problem
I am using jasmine to unit test an angularjs controller that sets a variable on the scope to the result of calling a service method that returns a promise object: ``` var MyController = function($scope, service) { $scope.myVar = service.getStuff(); } ``` inside the service: ``` function getStuff() { return $http.get( 'api/stuff' ).then( function ( httpResult ) { return httpResult.data; } ); } ``` This works fine in the context of my angularjs application, but does not work in the jasmine unit test. I have confirmed that the "then" callback is executing in the unit test, but the $scope.myVar promise never gets set to the return value of the callback. My unit test: ``` describe( 'My Controller', function () { var scope; var serviceMock; var controller; var httpBackend; beforeEach( inject( function ( $rootScope, $controller, $httpBackend, $http ) { scope = $rootScope.$new(); httpBackend = $httpBackend; serviceMock = { stuffArray: [{ FirstName: "Robby" }], getStuff: function () { return $http.get( 'api/stuff' ).then( function ( httpResult ) { return httpResult.data; } ); } }; $httpBackend.whenGET( 'api/stuff' ).respond( serviceMock.stuffArray ); controller = $controller( MyController, { $scope: scope, service: serviceMock } ); } ) ); it( 'should set myVar to the resolved promise value', function () { httpBackend.flush(); scope.$root.$digest(); expect( scope.myVar[0].FirstName ).toEqual( "Robby" ); } ); } ); ``` Also, if I change the controller to the following the unit test passes: ``` var MyController = function($scope, service) { service.getStuff().then(function(result) { $scope.myVar = result; }); } ``` Why is the promise callback result value not being propagated to $scope.myVar in the unit test? See the following jsfiddle for full working code http://jsfiddle.net/s7PGg/5/