I am trying to unit test an AngularJS service that uses $resource
. To keep it isolated, I would like to use Jasmine's spyOn and spy on the $resource
's query()
method. In my controller, I would like to be able to use the short form of query()
where you pass a success and an error function directly to the query method without calling $promise.then(success, error)
. Is this possible, or am I stuck with the long form of query().$promise.then(success, error)
?
Here is a plunker I created with a failing test illustrating my problem: http://plnkr.co/edit/hVc2YNnwUDNv7IHODOMD?p=preview
I have found several questions on SO claiming to solve the problem, but all are using much older versions of the components I am using. From the plunker you can see I'm working with Angular 1.5.2, and Jasmine 2.4.1.
As a related question, a number of tutorials show that in your controller, you can just assign the return value of query()
to an array, and as the data is loaded, the array will be updated. This is the cleanest solution, but what happens if an error occurs? I would expect if there is a problem loading the data, you just end up with either some default error notification, or nothing happening at all. Is the best practice to handle errors elsewhere via an interceptor and maybe fire an event and notify the user in some generic non-controller specific way? I think then the interceptor would need some way of determining what message to display to the user to give some context, eg 'Loading of Bagels seems to be taking longer than usual, click here to retry' as opposed to 'some request returned a 500 status code'
You should still be able to use the function shorthand where you pass in the functions as query(success, error). To fix the unit test you need to account for the error function as shown below:
spyOn(mockBagelApiService, 'query').and.callFake(function(callback1, callback2) {
queryDeferred.promise.then(callback1);
queryDeferred.promise.catch(callback2);
return {$promise: queryDeferred.promise}
});
callFake
will take in the parameters you pass into query()
which are two functions for success and error. Depending on whether the promise has been resolved or rejected you need to handle the then and catch blocks with the appropriate callback.
The way i've handled request errors in my angular apps is by creating an error handler service that gets called in the catch block and will take in a specific error message and pop up a modal. You can configure it however you want: you can define the modal buttons to reload or redirect etc. For handling the message, you can configure your services to return problem codes that give a description of the situation such as USER_IS_NOT_AUTH
or OAUTH_ERROR
that your error handler can use to provide a more specific reponse.