I'm trying to spy on an $emit from a directive, but somehow I cannot get the spy to 'hear' the $emit.
This is the code in my directives' controller:
$scope.$on('send', function () {
console.log('called');
$scope.$emit('resultSend', {'ok': true, 'data': ''});
});
This is my unit test:
var $rootScope, $compile, elm, element;
beforeEach(inject(function ($injector) {
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
elm = angular.element('<test></test>');
element = $compile(elm)($rootScope);
}));
it('should listen for the send broadcast and emit the resultSend', function () {
spyOn($rootScope, '$emit');
$rootScope.$broadcast('send');
expect($rootScope.$emit).toHaveBeenCalledWith('resultSend');
});
The console.log output ('called') is printed out by Karma, so I guess the unit test broadcast event does work.
Does this have to do with $emit not broadcasting down but up, and if so, how do I catch it, and if not, how else do I handle this case?
If your directive has a controller, you could, and should, test that separately from the directive. That's the whole point of an MVC architecture, you can test the C seperately from the V. ;)
That said, it would be a plain-Jane controller test spec.
Another tip: You should do all of your set up in your beforeEach() block (i.e. spies and whatever) and then do the assertions in your it() blocks.
Finally: Make sure that the spy you're setting up, is on the scope you're passing into the controller you're testing.
According to the docs here, you are correct in your understanding of the difference between $emit
and $broadcast
. However, I think the problem is in your use of $scope
and $rootScope
. Your $rootScope
will be at the top level of your scope hierarchy. I'm guessing (just by looking at your snippets without being able to see all the code) that your $scope
in your controller is a nested controller, meaning that $scope
in your controller is a child of the app's $rootScope
.
Because of that, when your unit test spys on the $rootScope.$emit
function, it is not actually spying on your controller's $scope.$emit()
call. Those two "scopes" are different, not the same thing. So, you need to mock the $scope
that you provide for the controller and then do a spyOn
on that.
For example, in your beforeEach
:
var ctrl, scope;
beforeEach(function() {
module('<INSERT YOUR CONTROLLERS MODULE NAME HERE>');
inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('<CTRL NAME HERE>', {$scope: scope});
});
});
This code will actually create a "mock" scope variable and will provide that object to your controller, which you can then do spies and other things with. Such as:
spyOn(scope, '$emit');
// do whatever triggers the "$emit" call
expect(scope.$emit).toHaveBeenCalledWith('resultSend');
I'm pretty sure that should fix your problem. Let me know if that needs more explanation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With