I have a dependency injection (understanding) problem while testing a directive (AjaxLoader displayed only when there is a pending request).
App declaration :
angular.module('app', [
'directives.ajaxLoader',
'services.httpRequestTracker',
[...]
])
Directive code :
angular.module('directives.ajaxLoader', [])
.directive('ajaxLoader', ['httpRequestTracker',
function(httpRequestTracker) {
return {
templateUrl: 'common/ajaxLoader.tpl.html',
link: function($scope) { // This function can have more parameters after $scope, $element, $attrs, $controller
$scope.hasPendingRequests = function() {
return httpRequestTracker.hasPendingRequests();
};
}
};
}
])
Test code :
describe('ajaxLoader', function() {
beforeEach(function() {
module('directives.ajaxLoader', 'common/ajaxLoader.tpl.html');
});
describe('ajaxLoader directive', function() {});
});
From there, my directive works perfectly well in the browser, but tests fails with an error like :
Error: [$injector:unpr] Unknown provider: httpRequestTrackerProvider <- httpRequestTracker <- ajaxLoaderDirective
Ok, so I need to inject my dependency somewhere. I have two solutions :
angular.module('directives.ajaxLoader', [ 'services.httpRequestTracker' ])
beforeEach(function() { module('directives.ajaxLoader', 'common/ajaxLoader.tpl.html', 'services.httpRequestTracker'); });
Both works, but I don't understand which one is the better and why ? And why is it working in my browser from the start and fails in my test ? In both case, all my directives and trackers are injected in my main app declaration
Thanks
Loading modules
It works in your application because services.httpRequestTracker
is loaded. you did that by declaring it as a dependency of the main app module (your first code snippet).
However, when you test things, you want to mock everything that is not being tested to avoid biass. In your case, what if you had a problem in services.httpRequestTracker
? ajaxLoader
might be fine but your tests will fail.
Mocking
To mock everything else, you have two options:
To use a dependency, you have to load the module with module()
.
you will have to load the dependency, but this might have a mock implementation.
Dependency Injection
Dependency injection lets you decouple classes. There is a service locator to resolve dependencies by name. That is, you say attribute a of class C is of type 'animal' (a string!). The service locator in the angular core finds which component implements it. A way of determining this is by looking up the loaded modules (e.g. dependencies of the main app module).
You didn't define any of this in your testing area (but it isn't a problem!). Karma uses a file karma.conf
that contains a list of files to use. You might use this file to add libraries or mocked components.
With your particular problem:
The directive depends on httpRequestTracker
. If you don't inject it there, it won't work (so it's ok). In your test, you have to load both. That's why the first time it failed and the second it worked. However, instead of loading httpRequestTracker
, I'd load a mock implementation of it.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments