I have a module that sends an error via $log.error(). This is working and I'm trying to finish the tests for it. My problem is that I'm unable to validate that it logs an error during my spec.
code:
var app = angular.module('plunker', []);
app.provider('MordorProvider', function($logProvider, $injector) {
// we gotta do this cuz we're a provider and don't have access to $log yet
var $log = angular.injector(['ng']).get('$log');
this.makeMeAnErrorWorthyOfMordor = function () {
$log.error('You shall not pass!');
}
this.$get = function () {
return this;
};
});
test:
describe('Gandalfs last stand', function() {
var MordorProvider,
$log;
beforeEach(module('plunker'));
beforeEach(inject(function(_MordorProvider_, _$log_) {
MordorProvider = _MordorProvider_;
$log = _$log_;
}));
it('should shout out!', function() {
MordorProvider.makeMeAnErrorWorthyOfMordor();
expect($log.error.logs).toContain('You shall not pass!');
});
});
I understand that my spec is using ngMock so that I have access to $log.error.logs, but if it doesn't have access to the same $log data, I'm confused on how to test this. If you open the console on that plunkr, you can see that the error is being emitted.
angular.injector
creates a new injector instance and thus, new $log
service instance. It isn't supposed to be used inside of Angular app in production, there are too few valid use cases for it.
This
app.provider('MordorProvider', function($logProvider, $injector) {
// we gotta do this cuz we're a provider and don't have access to $log yet
var $log = angular.injector(['ng']).get('$log');
...
});
can't be considered a workaround to use $log
in provider. There's simple a reason why service instances aren't available for injection in service providers - they just aren't instantiated yet.
In this case $log
variable refers to $log
instance which can't be injected anywhere else and can't be tested. This is why the spec fails on $log.error.logs
.
There's nothing in this service that would require provider
instead of factory
. When $log
is injected and used in normal way, it can be safely tested with
beforeEach(module('plunker', {
$log: {
error: jasmine.createSpy()
}
}));
...
expect($log.error).toHaveBeenCalledWith('You shall not pass!');
Since the usage of $window
service is the only thing that makes $log
unsuitable for injection into config
and provider
, it can be cloned and modified to evade this limitation:
angular.module('uniformLog', []).config(function ($provide, $injector, $logProvider) {
var uniformLog = $injector.invoke($logProvider.$get, null, { $window: window });
$provide.constant('uniformLog', uniformLog);
// or $provide.constant('$log', ...) to override it entirely
});
angular.module('app', ['uniformLog']).config(function ($logProvider, uniformLog) {
$logProvider.debugEnabled(false);
uniformLog.debug('...');
});