Injecting a mock into an AngularJS service
You can inject mocks into your service by using $provide
.
If you have the following service with a dependency that has a method called getSomething:
angular.module('myModule', [])
.factory('myService', function (myDependency) {
return {
useDependency: function () {
return myDependency.getSomething();
}
};
});
You can inject a mock version of myDependency as follows:
describe('Service: myService', function () {
var mockDependency;
beforeEach(module('myModule'));
beforeEach(function () {
mockDependency = {
getSomething: function () {
return 'mockReturnValue';
}
};
module(function ($provide) {
$provide.value('myDependency', mockDependency);
});
});
it('should return value from mock dependency', inject(function (myService) {
expect(myService.useDependency()).toBe('mockReturnValue');
}));
});
Note that because of the call to $provide.value
you don't actually need to explicitly inject myDependency anywhere. It happens under the hood during the injection of myService. When setting up mockDependency here, it could just as easily be a spy.
Thanks to loyalBrown for the link to that great video.
The way I look at it, there's no need to mock the services themselves. Simply mock the functions on the service. That way, you can have angular inject your real services as it does throughout the app. Then, mock the functions on the service as needed using Jasmine's spyOn
function.
Now, if the service itself is a function, and not an object that you can use spyOn
with, there's another way to go about it. I needed to do this, and found something that works pretty well for me. See How do you mock Angular service that is a function?
Another option to help make mocking dependencies easier in Angular and Jasmine is to use QuickMock. It can be found on GitHub and allows you to create simple mocks in a reusable way. You can clone it from GitHub via the link below. The README is pretty self explanatory, but hopefully it might help others in the future.
https://github.com/tennisgent/QuickMock
describe('NotificationService', function () {
var notificationService;
beforeEach(function(){
notificationService = QuickMock({
providerName: 'NotificationService', // the provider we wish to test
moduleName: 'QuickMockDemo', // the module that contains our provider
mockModules: ['QuickMockDemoMocks'] // module(s) that contains mocks for our provider's dependencies
});
});
....
It automatically manages all of the boilerplate mentioned above, so you don't have to write out all of that mock injection code in every test. Hope that helps.
In addition to John Galambos' answer: if you just want to mock out specific methods of a service, you can do it like this:
describe('Service: myService', function () {
var mockDependency;
beforeEach(module('myModule'));
beforeEach(module(function ($provide, myDependencyProvider) {
// Get an instance of the real service, then modify specific functions
mockDependency = myDependencyProvider.$get();
mockDependency.getSomething = function() { return 'mockReturnValue'; };
$provide.value('myDependency', mockDependency);
});
it('should return value from mock dependency', inject(function (myService) {
expect(myService.useDependency()).toBe('mockReturnValue');
}));
});
If your controller is written to take in a dependency like this:
app.controller("SomeController", ["$scope", "someDependency", function ($scope, someDependency) {
someDependency.someFunction();
}]);
then you can make a fake someDependency
in a Jasmine test like this:
describe("Some Controller", function () {
beforeEach(module("app"));
it("should call someMethod on someDependency", inject(function ($rootScope, $controller) {
// make a fake SomeDependency object
var someDependency = {
someFunction: function () { }
};
spyOn(someDependency, "someFunction");
// this instantiates SomeController, using the passed in object to resolve dependencies
controller("SomeController", { $scope: scope, someDependency: someDependency });
expect(someDependency.someFunction).toHaveBeenCalled();
}));
});