I am trying to write a unit test for my custom directive:
.directive('uiList', [
function(scriptingService) {
return {
scope: {
lengthModel: '=uiList'
},
link: function(scope, elm, attrs) {
scope.$watch('lengthModel', function() {
scriptingService.getScript(request).then(function(scripts) {
//something
});
});
}
};
}
Inside I call a service:
.service('scriptingService', function() {
var scriptingService = {
getScript: function() {
return 'blaat';
}
};
return scriptingService;
})
I would like to test whether the getScript method is called so I wrote this test;
beforeEach(inject(function($rootScope, $compile, _scriptingService_) {
scope = $rootScope.$new();
scope.row = 1;
scriptingService = _scriptingService_;
spyOn(scriptingService, 'getScript');
element = angular.element('<ul id="rows" ui-list="row">');
$compile(element)(scope);
scope.$digest();
elementScope = element.scope();
}));
it('should call service', function() {
scope.$apply(function() {
scope.row = 2;
});
expect(scriptingService.getScript).toHaveBeenCalled();
});
At the moment I get an error:
TypeError: Cannot read property 'getScript' of undefined.
Why do I get this error and how can I fix it? I thought I mocked the service out already?
plunkr: http://plnkr.co/edit/IQOCut?p=preview
Clean version -
http://plnkr.co/edit/8SVlzG?p=preview
beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope.$new();
scope.row = 1;
element = angular.element('<ul id="rows" ui-list="row">');
$compile(element)(scope);
scope.$digest();
elementScope = element.scope();
}));
it('should call service', function() {
scope.$apply(function() {
scope.row = 2;
});
expect(elementScope.test).toEqual(1);
});
Related
I have the following directive. This Directive will trigger after animation end triggers. I now just want to add test for this directive. I have created the test case for this. But it's failing. Can anybody help on this? This is plunker
angular.module('movieApp')
.directive('animationend', function() {
return {
restrict: 'A',
scope: {
animationend: '&'
},
link: function(scope, element) {
var callback = scope.animationend(),
events = 'animationend webkitAnimationEnd MSAnimationEnd' +
'transitionend webkitTransitionEnd';
element.on(events, function(event) {
console.log('[animationend directive] callback');
callback.call(element[0], event);
});
}
};
});
And my test case is
describe('Directive: animationend', function () {
// load the directive's module
beforeEach(module('movieApp'));
var elements,
scope,
compile;
beforeEach(inject(function ($rootScope, _$compile_) {
scope = $rootScope.$new();
compile = _$compile_;
elements = angular.element('<div animationend="" ></div>');
elements = compile(elements)(scope);
scope.$digest();
}));
it('should trigger the callback once animationclass is added ', function () {
scope.ctrl = {
animationend: jasmine.createSpy('animationend')
};
scope.$digest();
var events = 'animationend webkitAnimationEnd MSAnimationEnd' +
'transitionend webkitTransitionEnd';
angular.element(elements).triggerHandler(events);
// element.trigger(events);
expect(scope.ctrl.animationend).toHaveBeenCalled();
});
});
I'm trying to change the return value of a method in my mock service, but the new method is never called.
The code:
describe('Test 1', function() {
var ctrl, scope, mySrvMock;
beforeEach(function() {
mySrvMock = {
method: function() {
return 'value';
}
}
});
beforeEach(function() {
module('app');
inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('mainCtrl', {
$scop: scope,
mySrv: mySrvMock
});
});
});
it('should return value', function() {
expect(scope.callToMethod).toBe('value') // Pass
});
describe('Test 1.1', function() {
beforeEach(function() {
mySrvMock.method = function() {
return 'different value';
};
});
it('should return different value', function() {
expect(scope.callToMethod).toBe('different value') // Fail (Expected 'value' to be 'different value')
});
});
});
There is a way to listen to the mock service changes?
initialize controller before each it()
describe('Test 1', function() {
var ctrl, scope, mySrvMock, controllerFactory;
beforeEach(function() {
mySrvMock = {
method: function() {
return 'value';
}
}
});
beforeEach(function() {
module('app');
inject(function($rootScope, $controller) {
scope = $rootScope.$new();
controllerFactory = $controller.bind(this, 'mainCtrl',{
$scop: scope,
mySrv: mySrvMock
});
ctrl = controllerFactory();
});
});
describe('Test 2.1', function() {
beforeEach(function() {
ctrl = controllerFactory();
});
it('should return value', function() {
expect(scope.callToMethod).toBe('value') // Pass
});
});
describe('Test 1.1', function() {
beforeEach(function() {
mySrvMock.method = function() {
return 'different value';
};
ctrl = controllerFactory();
});
it('should return different value', function() {
expect(scope.callToMethod).toBe('different value') // Fail (Expected 'value' to be 'different value')
});
});
});
I've simple directive with an event handler.
angular.module('app')
.directive('acc', function () {
return {
restrict: 'A',
link: function (scope, element) {
scope.a = 0;
scope.func = function() {
console.log('h2.clicked !!');
scope.a = 1;
};
element.on('click', 'h2', scope.func);
}
};
});
And simple test
describe('Directive: acc', function () {
beforeEach(module('app'));
var element,
scope;
var clickContext = 'h2';
var onSpy;
beforeEach(inject(function ($compile, $rootScope) {
scope = $rootScope.$new();
onSpy = spyOn($.fn, 'on').and.callThrough();
element = angular.element('<nav acc><h2></h2></nav>');
element = $compile(element)(scope);
scope.$digest();
}));
it('should set click handler', function () {
expect(onSpy).toHaveBeenCalledWith('click', clickContext, jasmine.any(Function));
});
describe('click handler behaviour', function () {
beforeEach(function () {
element.find('h2').triggerHandler('click');
});
it('handler should be called', function () {
expect(scope.a).toBe(1);
});
});
});
I want to invoke handler before each test case for verify behaviour.
How to do it?
I use jasmine for unit testing.
Have you tried calling scope.$digest(); after the .click()?
I want to test the following directive "spinInput" which requires ngModel, but I can't access the directive's scope. All I get is an empty scope. Angularjs 1.3.13 is used.
Directive:
angular.module('charts.spinInput',[]).directive('spinInput', function() {
return {
restrict: 'E',
replace: true,
require:'ngModel',
scope: {
min:"#"
},
controller:function($scope)
{
$scope.test=12;
$scope.calcAngle=function(point)
{
var xDif=point.x-50;
var yDif=point.y-50;
return (Math.atan2(yDif, xDif) * 180 / Math.PI);
};
},
templateUrl: 'charts/directive/spinInput/spinInput.html',
link:function(scope, element, attr,ngModel) {
...
}
};
});
Unit Test:
throws following error: TypeError: 'undefined' is not an object (evaluating 'innerScope.min')
describe('spinInput', function() {
beforeEach(module('charts'));
var $httpBackend;
var element;
var outerScope;
var innerScope;
beforeEach(inject(function($rootScope, $compile , $injector) {
element = angular.element('<spin-input min="12"></spin-input>');
$httpBackend = $injector.get('$httpBackend');
$httpBackend.whenGET('charts/directive/spinInput/spinInput.html').respond(200, '');
outerScope = $rootScope.$new();
$compile(element)(outerScope);
innerScope = element.isolateScope();
outerScope.$digest();
}));
it('scope.min should be defined', function() {
expect(innerScope.min).toBeDefined();
});
});
The way you are constructing your test seems to be causing issues.
I have been able to successfully test the isolated scope as per below.
You can view the test running on this jsfiddle (with templates code commented out).
describe('Directive: spinInput', function() {
var scope, compile, validHTML, templateHtml;;
validHTML = '<spin-input min="12"></spin-input>';
beforeEach(module('myApp'));
beforeEach(inject(function($compile, $rootScope, $templateCache){
templateHtml = $templateCache.get('charts/directive/spinInput/spinInput.html');
if(!templateHtml) {
templateHtml = $.ajax('charts/directive/spinInput/spinInput.html', {async: false}).responseText;
$templateCache.put('charts/directive/spinInput/spinInput.html', templateHtml)
}
scope = $rootScope.$new();
compile = $compile;
}));
function create() {
var elem, compiledElem;
elem = angular.element(validHTML);
compiledElem = compile(elem)(scope);
scope.$digest();
return compiledElem;
}
it('scope.min should be defined', function() {
var el = create();
expect(el.isolateScope().min).toBeDefined();
});
it('scope.min should equal 12', function() {
var el = create();
expect(el.isolateScope().min).toEqual('12');
});
});
Try putting the outerScope.$digest() before element.isolateScope()
How do I check if $emit has been called in an unit test of a directive?
My directive is fairly simple (for the purpose of illustration):
angular.module('plunker', [])
.directive('uiList', [function () {
return {
scope: {
lengthModel: '=uiList',
},
link: function (scope, elm, attrs) {
scope.$watch('lengthModel', function (newVal) {
scope.$emit('yolo');
});
}
}
}])
so every time the attribute uiList changes, it will emit the event.
And I have unit test code as follows:
describe('Testing $emit in directive', function() {
var scope;
var element;
//you need to indicate your module in a test
beforeEach(function () {
module('plunker');
inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
scope.row= 1;
spyOn(scope,'$emit');
element = angular.element('<ul id="rows" ui-list="row">');
$compile(element)(scope);
});
});
it('should emit', function() {
scope.$digest();
scope.row = 2;
scope.$digest();
expect(scope.$emit).toHaveBeenCalledWith("yolo");
});
});
This would always give me error stating that the scope.$emit has never been called.
Is there something wrong with the scope? Can someone please help?
Plunker:http://plnkr.co/edit/AqhPwp?p=preview
Your directive creates an isolated scope, which is calling $emit,so you need to spy on this one ;)
describe('Testing $emit in directive', function() {
var scope;
var element;
var elementScope;
beforeEach(module('plunker'));
beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope.$new();
scope.row = 1;
element = angular.element('<ul id="rows" ui-list="row">');
$compile(element)(scope);
scope.$digest();
elementScope = element.scope();
}));
it('should emit', function() {
// arrange
spyOn(elementScope, '$emit');
// act
scope.$apply(function() {
scope.row = 2;
});
// assert
expect(elementScope.$emit).toHaveBeenCalledWith("yolo");
});
});
Fixed plunker here :)
I'm not sure how did it work for glepetre (not sure that it does actually). For me it didn't. The thing is that the directive runs $new on incomming scope so the scope it creates inside and element.scope() are some thing different. You can check their $id fields. So it does not help to spy on the scope you got from element.scope(). I had smilar problem and had to do a little trick to make it work.
Here comes my test :
beforeEach(inject(function () {
scope = $rootScope.$new();
scope.$apply(function () {
scope.field = null;
scope.name = 'nolength';
});
spyOn(scope, '$new').and.returnValue(scope);
spyOn(scope, '$emit');
var element = angular.element('<my-directive name="name" field="field" title="\'Title\'" ></my-directive>');
noLengthValidation = $compile(element)(scope);
scope.$apply();
elementScope = element.scope();
}));
So the trick was to mock $new function on scope so instead of creating a new scope behind the scene and mess the things up it will return itself! Here comes the test itself :
it('The directive should not react on string length if no max-chars attribute presented', function () {
scope.$apply();
noLengthValidation.isolateScope().field = 'fieldisssssssveeeeerryyyyyylooooooonnnngggggggggggggggggggggggggggg';
scope.$apply();
expect(scope.$emit).toHaveBeenCalledWith('ON_MORE_AWASOME_EVENT', 'nolength');
});
And the the part on the directive that calls $emit. Just in case :
function validate() {
scope.validate = 1;
if (scope.field) {
if (!isValid()) {
scope.$emit('ON_AWASOME_EVENT', {code : scope.name, text : 'Field ' + scope.title + ' is feels bad'});
} else {
scope.$emit('ON_MORE_AWASOME_EVENT', scope.name);
}
}
}