Testing .on in directive expected spy got function - angularjs

I am trying to test the reciever from a $broadcast (from a controller) in my directive via .on.
Directive:
describe('<-- myDirective Spec ------>', function () {
var scope, $compile, element, $httpBackend, rootScope;
beforeEach(angular.mock.module('myApp'));
beforeEach(inject(function (_$rootScope_, _$compile_, _$httpBackend_) {
scope = _$rootScope_.$new();
$compile = _$compile_;
$httpBackend = _$httpBackend_;
rootScope = _$rootScope_;
var html = '<my-directive></my-directive>';
element = $compile(angular.element(html))(scope);
spyOn(scope, '$broadcast').and.callThrough();
scope.$digest();
}));
it('should be defined', function () {
expect(element).toBeDefined();
});
it('should broadcast ', function () {
scope.$broadcast('sendEmail');
expect(scope.$on).toHaveBeenCalledWith('sendEmail', jasmine.any(Function));
});
});
With the above, i get error:
Expected a spy, but got Function.

Update:
You can either test simply if your $broadcast is getting called with
expect(scope.$broadcast).toHaveBeenCalled()
or to actually test $on do something like
scope.$on('sendEmail', spyFunction)
expect(spyFunction).toHaveBeenCalledWith('sendEmail')
Reason: $broadcast doesn't actually calls $on function. $on is a listener which calls the callback function (passed as second argument) when it listens the event (first argument).
You are currently spying on $broadcast function of the scope, and have put a test on $on function. You need to replace
spyOn(scope, '$broadcast').and.callThrough();
by
spyOn(scope, '$on').and.callThrough();

Related

Testing AngularJS directive scope undefined

I'm trying to test one of my directives and I can't work out why my scope object is undefined.
define(["require", "exports", 'angular', 'directives', "angularMocks", "chartApp"], function (require, exports, angular, Directives) {
"use strict";
describe('board controls', function () {
describe('task filter', function () {
var $compile;
var $rootScope;
var scope;
var element;
beforeEach(angular.mock.module('chartApp'));
beforeEach(angular.mock.module('partials/directives/board-controls.html'));
beforeEach(inject(function (_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
expect(scope).toBeDefined();
element = $compile('<board-controls></board-controls>')(scope);
scope.$digest();
}));
it('displays modal', function () {
scope.showChildFilters();
});
});
});
});
In the it('displays modal')... part Karma outputs:
TypeError: undefined is not an object (evaluating 'scope.showChildFilters')
But in the beforeEach(...) part it seems to be working. I just can't see why this doesn't work.
You need to change to this
it('displays modal', function () {
//scope.showChildFilters();
var isolateScope = element.isolateScope(); //I prefer to name isolateScope
isolateScope.$apply() //cause scope to digest and watch and all that
isolateScope.showChildFilters();
});
This is a very detailed answer to your question as well.
Testing element directive - can't access isolated scope methods during tests

AngularJS - Unit Test with $timeout

I have a directive that look like this:
angular.directive('myDirective', ['$compile','$timeout', function($compile,$timeout){
console.log("myDirective compile");
return {
link: function(scope, element) {
console.log("myDirective before timeout");
$timeout(function(){
console.log("myDirective after timeout");
});
}
};
}]);
When I do unit test with Karma-Jasmine, I get the LOG output of myDirective compile and myDirective before timeout. But no output for myDirective after timeout
How can I get the $timeout function to run in unit test?
You can use the flush method on the $timeout service to flush the timeouts that you set. https://docs.angularjs.org/api/ngMock/service/$timeout
Note that this is available in ngMock and you therefore need to include angular-mocks. (I'm fairly sure you have but just in case.)
See the test below for an example.
describe('my directive test', function () {
var element;
var scope;
beforeEach(module('myApp'));
beforeEach(inject(function($compile, $rootScope){
element = '<my-directive></my-directive>';
scope = $rootScope.$new();
element = $compile(element)(scope);
scope.$digest();
spyOn(console, 'log').and.callThrough();
}));
it('should not log the timeout', inject(function ($timeout) {
expect(console.log).not.toHaveBeenCalledWith('myDirective after timeout');
}));
it('should log after timeout', inject(function ($timeout) {
$timeout.flush();
expect(console.log).toHaveBeenCalledWith('myDirective after timeout');
}));
});
Use
$timeout.flush(amount)
In your tests, with amount the amount of time you want to pass.

How to Stub a DOM object with AngularJs and/or Sinon

I'm trying to test an angular controller that queries the DOM for a select box value.
$scope.getValue = function(){
document.getElementById('select_box_value').value;
}
How can I stub this function so it returns a simple integer value in a controller test, like so? Every time I try it this way, the original function runs instead of my stub.
describe('MyCtrl', function () {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope,
'$scope.getValue':sinon.stub().returns(20)
});
}));
it('gets the select box value', function () {
expect(scope.getValue).toBe(20);
});
});

AngularJS Unit testing a directive that needs the $compile provider

I'm trying to test a directive.
This directive use the $compile provider.
I would try to expect if $compile will been called but providing it in the test I'm getting this error:
TypeError: 'undefined' is not a function (evaluating '$compile(angular.element(html))(scope)')
I know why is that happening (I'm overriding with a fake mock the actual $compile provider) but I don't really know how can I fix that problem.
This is the actual test code:
describe('directive', function () {
var scope, mockCompile;
beforeEach(function () {
mockCompile = jasmine.createSpy();
module('directive', function ($provide) {
$provide.value('$compile', mockCompile);
});
var html = '<div directive="foo"></div>';
// The problem is there. I'm injecting the mocked compile service
// Not the real one
inject(function ($compile, $rootScope) {
scope = $rootScope.$new();
$compile(angular.element(html))(scope);
scope.$digest();
});
});
it("should test the directive", function () {
//Act.
scope.$apply();
//Assert.
expect(mockCompile).toHaveBeenCalled();
});
});
First, make sure you have included angular-mocks.js for testing.
Then, each service you mock needs to begin and end with an underscore;
inject(function ($compile, $rootScope)
Should really by;
inject(function (_$compile_, _$rootScope_)

Testing Directives events

I have a directive that $emit's an event, but in the parent scope of a unit test doesn't get the event.
test $broadcasts an event to the directive, directive gets the event, and then directives $emits its own event, that the parent should get.
unit test:
beforeEach(inject(function ($compile, $rootScope) {
scope = $rootScope.$new();
compile = $compile;
}));
element = compile(angular.element('<div myDirective></div>'))(scope);
scope.$digest();
var _scope = element.isolateScope();
scope.$broadcast('directive-change');
scope.$on('directive-successfully-changed', function (e) {});
directive:
scope.$on('directive-change', function (e) {
scope.$emit('directive-successfully-changed');
});

Resources