Dependencies are one of the most widely used mechanisms in AngularJS. You see them everywhere, from dependency injection to global services. This can present a problem when it comes to unit tests because you don’t really want to have to test the full functionality of the dependency within the unit test. Instead, you will want to get the dependency and control it with your own mock objects and services.
There are four methods for controlling the dependency injection:
■ Create your own instance of the dependency using the new
operator.
■ Create the dependency as a global object you can look up anywhere.
■ Ask a registry for it. This option requires that you have access to the registry as well, which means also placing it in a global location.
■ Have the dependency passed to you.
The following sections describe each of these methods in AngularJS.
Consider the following function and assume that MyService
is a global service that you can create an instance of:
function MyClass() {
this.doSomething = function() {
var gSrv = new MyService();
var data = sSrv.getSomething();
}
}
To control MyService
you would need to do something like this:
var savedMyService = MyService;
MyService = function MockMyService() {};
var myClass = new MyClass();
myClass.doSomething();
MyService = savedMyService;
This works; however, you run the risk of losing the handle to MyService
if things go wrong, so you need to be careful.
This is similar to using the new
operator. Consider the following function and assume that global.myService
is a singleton instance of the MyService
service you need:
function MyClass() {
this.doSomething = function() {
var data = global.myService.getSomething();
}
}
To control MyService
you would need to do something like this:
var savedMyService = global.myService;
global.myService = function MockMyService() {};
var myClass = new MyClass();
myClass.doSomething();
global.myService = savedMyService;
Again, this works, but because you run the risk of losing the handle to global.myService
if things go wrong, you need to be careful.
Consider the following function and assume that global.serviceRegistry
is a singleton instance of a registry that has the MyService
service registered:
function MyClass() {
this.doSomething = function() {
var myService = global.serviceRegistry.get('MyService'),
var data = myService.getSomething();
}
}
To control MyService
you would need to do something like this:
var savedRegistry = global.serviceRegistry;
//create new globel.serviceRegistry
global.serviceRegistry.set('MyService', function MockMyService() {});
var myClass = new MyClass();
myClass.doSomething();
global.serviceRegistry = savedRegistry;
Again, this works, but because you run the risk of losing the handle to global.service-Registry
if things go wrong, you need to be careful.
I showed you the first three methods so that when you see the passed parameter method you will recognize that this is really the way you should be doing it. You should try to design your dependency usage such that the dependency can be passed in to the consumer. That way you can easily control the dependency from the test.
Now, consider the following function and assume that myService
is an instance of a service that is typically passed in:
function MyClass() {
this.doSomething = function(myService) {
var data = myService.getSomething();
}
}
To control MyService
all you need to do is the following:
var mockedService = function MockMyService() {};
var myClass = new MyClass();
myClass.doSomething(mockedService);