The Angular $http service provides an easy way to interact with an API endpoint. If you want to query some data, you can simply do this:
The request object above is an Angular HTTP promise (ng.IHttpPromise if you’re using TypeScript).
While easy enough to use, it can become a little problematic when unit testing.
Promise Service
Say we have a service that returns a promise:
And then we have a controller that uses this service:
When you want to test this (as you should), it’s a little tricky to mock the promise return type of the service.
So we’ll build a helper to do the main bits.
Building a Promise
For our helper class definition, we want the following public methods:
constructor: create a new helper
getHttpPromiseMock: get a mock promise return type
resolve: trigger promise fulfilment with data
reject: trigger promise rejection
To build up our own mock promise, we’ll use Angular’s $q service. This will let us define our return data and when to fulfil/reject the promise.
For triggering fulfilment, we’ll also need access to $rootScope.$apply() as this is integrated with $q.
constructor
Our constructor will use Angular’s $injector to get access to $q and $rootScope. We want an instance of the deferred object that we can work with within the class, so we create that here.
getHttpPromiseMock
For our mock object, we want to create something that looks and acts like a promise.
The success and error methods on our mock promise need to return the same mock instances, so we can pull out a method to wrap up the deferred promise as needed.
This is pretty much following the structure of the Angular wrapper.
We’re passing in a the promise property of a defer object.
We then create the return object that looks like the HTTP promise. Calls to then are just passed through directly to the created promise object from $q.
For success and error, we use different calls to $q‘s then method to simulate the required functionality, finally returning the wrapped promise.
resolve
To resolve the promise, we just need to pass in some data for the deferred‘s resolve function then call $apply on the $rootScope to propagate it.
reject
To reject the promise, we can call reject on the deferred and then $apply on the $rootScope.
Our full helper class looks like this:
So, now that we have that, how do we use it?
Testing Methods that Return a Promise
Just as a reminder, our controller looks like this:
We’ll need some test data along with our helper, so we’ll set these up in our beforeEach function of our Jasmine test. We’ll also create a new instance of the PromiseHelper.
Then, in our tests, we can use Jasmine mocking to create a version of our service.
Just for clarity, this is firstly creating a new Jasmine Spy object with the function getContacts.
Then we’re setting the return type of this Spy to return a mock HTTP promise from our helper class.
Putting this together with a full test then looks something like this:
On line 12, we call the resolve method to propagrate our contacts data then we can check the validity of the internal properties. Since the methods within the mock service are Jasmine Spies, we can explicitly check that the method has been called on line 18.
Testing for Rejection
If we wanted to test for a rejected promise, our test would look like this:
Our controller throws an error when the service rejects the promise, so we can check for this using Jasmine’s toThrow method on the promise call (lines 12-14).