I have written angular js directive one method, but I don't know how to write unit test for that.
var app = angular.module("myApp",[]);
app.directive('minMax', function() {
return {
require: 'ngModel',
link: function(scope, element, attr, mCtrl) {
function myValidation(value) {
if (value.toString().length > 2 & value.toString().length < 6) {
mCtrl.$setValidity('charE', true);
} else {
mCtrl.$setValidity('charE', false);
}
return value;
}
mCtrl.$parsers.push(myValidation);
}
};
});
How do I test this method?
Have a look here: https://github.com/daniellmb/angular-test-patterns.
It contains a great collection of test patterns.
Example of directive test:
describe('Directive: myDir', function () {
var element, scope, compile, defaultData,
validTemplate = '<my-dir ng-model="data"></my-dir>';
function createDirective(data, template) {
var elm;
// Setup scope state
scope.data = data || defaultData;
// Create directive
elm = compile(template || validTemplate)(scope);
// Trigger watchers
//scope.$apply();
// Return
return elm;
}
beforeEach(function () {
// Load the directive's module
module('myApp');
// Reset data each time
defaultData = 42;
// Provide any mocks needed
module(function ($provide) {
//$provide.value('Name', new MockName());
});
// Inject in angular constructs otherwise,
// you would need to inject these into each test
inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
compile = $compile;
});
});
describe('when created', function () {
// Add specs
});
describe('when the model changes', function () {
// Add specs
});
describe('when destroyed', function () {
// Add specs
});
});
Related
I used a directive to add a listener function on input file change, I'm going to create a unit test case for this in jasmine, but I'm not able to trigger function in unit case.
unit test code:
describe('customOnChange', function () {
beforeEach(module('app'));
beforeEach(inject(function ($rootScope, $compile, $parse) {
this.$rootScope = $rootScope;
this.compile = $compile;
this.parse = $parse;
this.initDirective = function (config) {
this.scope = this.$rootScope.$new();
this.scope.listnerFunction = function($event){
console.log("event called : ",$event);
};
this.element = angular.element('<input type="text" name="textField" id="textField" dynamic-data-editor-custom-on-change="listnerFunction($event)" ng-model="testText"/>');
this.compile(this.element)(this.scope);
this.$rootScope.$digest();
$('body').append(this.element);
this.$rootScope.$digest();
};
}));
fit('Applied directive successfully', function() {
this.initDirective();
spyOn(this.scope,'listnerFunction');
var ele = this.element.find("input#textField").eq(0);
console.log('element', ele);
ele.triggerHandler('change');
this.$rootScope.$apply();
expect(this.scope.listnerFunction).toHaveBeenCalled();
});
});
The main directive code:
function () {
'use strict';
angular.module('app')
.directive('customOnChange', function ($parse) {
return {
restrict: 'A',
link: function ($scope, element, attrs, ngModel) {
var attrHandler = $parse(attrs.dynamicDataEditorCustomOnChange);
var handler = function (e) {
$scope.$apply(function () {
attrHandler($scope, { $event: e });
});
};
element[0].addEventListener('change', handler, false);
}
};
});
})();
I got the error on running test
Expected spy listnerFunction to have been called.
Please help me to resolve the test case for the directive.
In your test the problem is: spy is created after the method listnerFunction function is called, so you see the error.
You should replace:
this.scope.listnerFunction = function($event){
console.log("event called : ",$event);
};
with (andCallFake for jasmine < 2)
this.scope.listnerFunction =
jasmine.createSpy("listnerFunction")
.and.callfake(function($event){
console.log("event called : ",$event);
});
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'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'm using this construct:
Directive with a ControllerAs.
The Controller has a depencency on a Service which does REST requests.
The directive and the controller:
angular.module('app')
.directive('thingsList', function () {
return {
templateUrl: 'thingsListEntry-template.html',
restrict: 'A',
controller: 'thingsListController as ctrl'
};
})
.controller('thingsListController', function (thingsStorage) {
thingsStorage.getList().then(angular.bind(this, function (response) {
this.things = response;
}));
});
What I want to do now is to test the directive with a controller mock:
describe('things list test suite', function() {
describe('tests for the directive', function () {
var scope, render, element, mockController;
/**
* Mock the controller
*/
beforeEach(module('app', function ($provide, $controllerProvider) {
$controllerProvider.register('thingsListController', function () {
this.things = [];
mockController = this;
});
}));
beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope.$new();
var angularElement = angular.element('<div things-list></div>');
var compileFunction = $compile(angularElement);
render = function () {
element = compileFunction(scope);
$rootScope.$digest();
};
}));
it('should be empty without things', function() {
render();
expect(element[0].querySelectorAll('div.things-list-entry').length).toEqual(0);
});
What I would like to do next is to change the things in the controller mock and test that. I don't know how to do that
it('should contain 1 entry with 1 thing', function () {
mockController.things = [{'name':'1'}];
render();
expect(element[0].querySelectorAll('div.thing-list-entry').length).toEqual(1);
});
Here I'm setting mockController.things, but I'm not sure how to get to the mockController. The version above sets it in the mock setup. I also tried using scope.ctrl.things and couple other things but nothing works. Any suggestions?
Try scope.mockController.things instead of mockController.things.
I'm going through the process of refactoring my controller function into more streamlined ones in my directives.
Am reasonably new to Angular and am running into problems mocking and testing my promises within the directives.
Within the function, I call a Box.reboot() from the directive rebootBox.
app.directive("rebootBox", ["Box", function(Box) {
return {
restrict: "A",
link: function( scope, element, attrs ) {
element.bind( "click", function() {
Box.reboot({id: scope.box.slug}).$promise.then(function(results) {
scope.box.state = 'rebooting';
}, function(errors) {
scope.box.errors = true;
})
});
}
}
}])
My tests pass in the controller specs because I am able to do something like this:
fakeFactory = {
reboot: function () {
deferred = q.defer();
return {$promise: deferred.promise};
}
...
}
MainCtrl = $controller('MainCtrl', {
$scope: scope,
Box: fakeFactory,
});
However, I can't get my head around how I am supposed to do this in my directive test?
I've tried this but I don't understand how I can mock what I did in the controller, ie:
Box: fakeFactory
My directive test looks like this so far:
describe('box reboot', function () {
var $scope,
element,
deferred,
q,
boxFactory;
beforeEach(module('myApp'));
beforeEach(inject(function($compile, $rootScope, $q) {
$scope = $rootScope;
q = $q;
element = angular.element("<div reboot-box></div>");
$compile(element)($rootScope)
boxFactory = {
reboot: function () {
deferred = q.defer();
return {$promise: deferred.promise};
}
};
}))
it("should reboot a box", function() {
spyOn(boxFactory, 'reboot').andCallThrough()
$scope.box = {}
element.click();
deferred.resolve({slug: 123});
$scope.$apply()
expect(boxFactory.reboot).toHaveBeenCalled();
});
...
Obvs. it fails because I'm spying on boxFactory.
What is the best way to go about testing such a function?
--- EDIT ----
Further to the comment below, I've used $provide to mock the service call:
beforeEach(module('myApp', function($provide) {
boxFactory = {
get: function () {
deferred = q.defer();
return {$promise: deferred.promise};
},
reboot: function () {
deferred = q.defer();
return {$promise: deferred.promise};
},
};
$provide.value("Box", boxFactory);
I can now call deferred.resolve successfully and all my tests pass bar one.
expect(boxFactory.reboot).toHaveBeenCalled();
Is there a specific reason why this fails and how can I get it to pass?