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_)
Related
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
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();
I am struggling to get the controller from within a directive for unit testing. Here is my angular app:
angular.module('test-app', [])
.controller('loadingCtr', ['$scope', function ($scope) {
}])
.directive('loading', function() {
return {
restrict: 'E',
controller: 'loadingCtr'
};
});
Here is my unit test code:
describe('loading', function () {
beforeEach(inject(function($rootScope, $compile) {
var fooElement = $compile('<loading></loading>')($rootScope);
var fooController = fooElement.controller('loading');
$rootScope.$digest();
console.log(fooController);
}));
describe('loading: testing loading directive', function() {
it('should create loading directive', function() {
});
});
});
Here is a plnkr to mess around with: http://plnkr.co/edit/38cc5HQFgeHDhnC8OMo8?p=info
fooController always returns as undefined. I've tried using the following examples I've found online, but I always get the same results:
Unit testing a directive that defines a controller in AngularJS
http://daginge.com/technology/2014/03/03/testing-angular-directive-controllers-with-jasmine-and-karma/
Is there something obvious here that I am missing?
Only issue i can see is that you are not loading the module test-app in your fixture, which means that the compiled html code does not really compile the directive loading since it is not available in the injector. So try loading the module in the beforeEach block. Loading the module ensures that directives, controllers, services etc registered under the module is available in the injector otherwise it will just use the module as ng which does not know anything about the loading directive.
i.e
describe('loading', function () {
var fooController;
//Load the module
beforeEach(module('test-app'));
beforeEach(inject(function($rootScope, $compile) {
var fooElement = $compile('<loading></loading>')($rootScope);
fooController = fooElement.controller('loading');
$rootScope.$digest();
console.log(fooController);
}));
describe('loading: testing loading directive', function() {
it('should create loading directive', function() {
expect(fooController).toBeDefined();
});
});
});
Demo
Also note that if you are registering the controller with .controller you can directly get the controller instance by $controller(ctrlName) construct. If you are using controllerAs syntax with bindToController:true in your directive then you can get it from the scope with the property name same as the alias as well.
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);
});
});
I'm using Videogular in an Angular app I'm working on. I wrote a plugin directive for it that listens to an event broadcast from $rootScope, and, if a video is playing, automatically pauses it when the event is broadcast.
omgYesDirectives.directive('vgAutoPause',
['$rootScope',
function($rootScope) {
return {
restrict: 'E',
require: '^videogular',
link: function($scope, $elem, $attr, $API) {
$rootScope.$on('onGameEnable', onGameEnable);
function onGameEnable(event, data)
{
$API.pause();
}
}
}
}
]);
But I'm having trouble figuring out how to unit test it. I can't seem to properly inject Videogular itself into my test. I've tried variations on this:
describe('vgAutoPause', function () {
var scope, compile, elm, videogular;
beforeEach(inject(function ($compile, $rootScope, videogularDirective) {
videogular = videogularDirective;
scope = $rootScope.$new();
compile = $compile;
}));
it('should instantiate as an HTML element', function () {
elm = compile('<videogular><vg-auto-pause></vg-auto-pause></videogular>')(scope);
scope.$digest();
expect(elm.html()).toContain('vg-auto-pause');
});
});
but Karma keeps complaining about it:
Error: [$injector:unpr] Unknown provider: videogularDirectiveProvider <- videogularDirective
Am I doing it wrong? Do you have any thoughts or suggestions on what I ought to be doing instead?
In AngularJS You can't inject a directive, you must create the HTML and then $compile it to start the $digest cycle.
For example, this is a simple videogular testing:
'use strict';
describe('Directive: Videogular', function () {
var element;
var scope;
beforeEach(module('myApp'));
beforeEach(inject(function ($compile, $rootScope) {
scope = $rootScope;
element = angular.element("<div><videogular><video></video></videogular></div>");
$compile(element)($rootScope);
}));
describe("videogular", function() {
it("should have videogular", function() {
scope.$digest();
expect(element.html()).toContain('<video></video>');
});
});
});
Maybe you need to understand first how to test directives, there's a lot of good info out there. You can start with this links:
http://docs.angularjs.org/guide/unit-testing
https://egghead.io/lessons/angularjs-unit-testing-a-directive
http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/