Reputation: 40337
We have a what we call a CORShttpService
, which is basically a wrapper aroung the $http
service, but encapsulates some CORS functionality that we need. I'm now writing some tests for a service that has the CORShttpService
injected into it. This service has code like this:
CORShttpService({method: requestMethod, url: getUrl(path), data: data}).
success(function(data, status, headers) {
//do success stuff
}).
error(function(data, status, headers) {
//do error stuff
});
I want to mock the call to CORShttpService
, but I'm not sure how to go about doing it. I'm using Jasmine, and its spyOn
function requires an object to mock the function on the object. My CORShttpService
isn't attached to any object, so I don't know how to go about mocking it. Yes, I could use $httpBackend
to mock the requests that eventually get set in the CORShttpService
, but I don't want it going into that service in the first place. I want to isolate the unit test and simply mock the external calls. Is there any way I can mock this service that is just a function?
Upvotes: 2
Views: 9005
Reputation: 40337
As I thought about this more, the $httpBackend
service does provide a lot of functionality for testing requests. As my CORShttpService
is a basically a wrapper around $http
, I decided I could probably get the most bang for my buck, if I made the mock implementation of CORShttpService
simple the $http
implementation. Using this documentation to help me, I have the following in my spec:
beforeEach(module(function($provide) {
$provide.provider('CORShttpService', function() {
this.$get = function($http) {
return $http;
};
});
}));
So, any of my services wanting the CORShttpService
injected, will now basically just have $http
injected, and thus allow me to use all the $httpBackend
functionality without the concern of the extra functionality found in the CORShttpService
itself.
This works for my specific case, but as far as a general solution for mocking services that are just a function, the same kind of thing could probably be done with jasmine.createSpy
as mentioned in zbynour's answer. Something like:
beforeEach(module(function($provide) {
$provide.provider('MyService', function() {
this.$get = function() {
return jasmine.createSpy("myService");
};
});
}));
Upvotes: 7
Reputation: 19817
Yes, as you wrote spyOn
can stub a function but it has to be property of already existing object. I also agree it's better to isolate the unit. :)
What you need is jasmine.createSpy
. By that you can create a bare spy and define its behavior as usual (using andReturn
, andCallFake
, etc.).
I think something like the code below (also with jasmine.createSpyObj
) cloud be what you want:
it ('should do sth', function() {
var res = jasmine.createSpyObj('res', [ 'success', 'error' ]);
res.success.andReturn(res);
res.error.andReturn(res);
var corsHttpService = jasmine.createSpy().andReturn(res);
// call the service using corsHttpService
anotherService(corsHttpService);
// assert
expect(corsHttpService).toHaveBeenCalledWith({
method: requestMethod,
url: url,
data: data});
expect(res.success).toHaveBeenCalledWith(jasmine.any(Function));
expect(res.error).toHaveBeenCalledWith(jasmine.any(Function));
});
In case you want to go further, instead of simple
res.success.andReturn(res);
you can store the callback passed in
var succCallback;
res.success.andCallFake(function(callback) {
succCallback = callback;
return res;
});
and later in the spec you can call the callback to emulate the success result from the corsHttpService
:
succCallback('a-data', 'a-status', 'a-header');
Then it's possible to test 'success stuff' of tested service...
Upvotes: 2