Ian McNally

Testing deferred objects in Angular

October 29, 2014

Consider the two code blocks below. They’re unit testing an asynchronous function service.getThing, that returns an initial promise, to be resolved at a later time.

So, try and spot the difference between the blocks. It’s a subtle detail that no one else eluded to on the web, that I could find at least. A careful read of Angular’s $q docs finally did it for me. (For the impatient, the correct solution is B).

A

thing = service.getThing() # returns deferred.promise
deferred.resolve someResponse # manual resolve, behind the scenes of `getThing`
$rootScope.$apply()
expect thing
  .toEqual expectedThing

B

thing = null
service.getThing() # returns deferred.promise
  .then (response) -> thing = response
deferred.resolve someResponse # manual resolve, behind the scenes of `getThing`
$rootScope.$apply()
expect thing
  .toEqual expectedThing

A is what I started with. I’m manually resolving the async request behind service.getThing. At this point, thing is a promise, and when I call $rootScope.$apply, thing should be updated to the resolve value someResponse.

That didn’t work. At that point, thing is still a promise. At that point, I was cursing the rootScope apply gods.

So, check out B. I’m initializing the response value thing, and updating it in a callback to service.getThing. What I’m not doing is reusing the promise. And this works. Do the rootScope apply, and thing is now your resolve value someResponse.

It’s subtle and a little bit of magic, but we made it. Good job, you.


Ian McNally

Hey, I'm Ian. I build websites and write about what I learn as I go. Follow me on Twitter.