I have a directive defined with local functions, for example :
angular.module('clientApp')
.directive('myDirective', function () {
return {
scope : { user_data : "=myDirective" } ,
link : function (scope) {
function is_valid_float() { return true; };
scope.function_on_scope = function () { return true; };
}
}
});
I'm trying to write a test for is_valid_float
describe('Directive: myDirective', function () {
// load the directive's module
beforeEach(module('clientApp'));
var element,scope;
beforeEach(inject(function ($rootScope,$compile) {
scope = $rootScope.$new();
element = angular.element('<div my-directive></div');
element = $compile(element)(scope);
scope.$digest();
}));
describe('Test', function () {
it('Should be defined', function() {
expect(element).toBeDefined();
expect(scope.function_on_scope).toBeDefined();
});
it('Should check float validity', function () {
expect(is_valid_float('123')).toBeTrue();
expect(is_valid_float('123.2.3')).toBeFalse();
});
});
});
So obviously this is not working. Both the local function is_valid_float and the function on the scope. I tried fetching the scope from the element but i'm getting an empty scope.
Testing directives in angular has always been a mystery to me so i'll appreciate some help with this. Thanks.
I'm using jasmine and karma.
Related
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
});
});
In my Controller I've defined the following service:
CrudService.getAllGroups().$promise.then(
function (response) { $scope.groups = response; },
function (error) { //error code.. }
);
Well, I want to test this service whether it gets a response or not. In test script at first I've defined a function to check whether the service is defined at all.
Test code:
describe('Ctrl: TestCtrl', function () {
beforeEach(module('testApp'));
var scope,
CrudService,
ctrl,
backend;
beforeEach(inject(function ($controller, $rootScope, _CrudService_, $httpBackend) {
scope = $rootScope.$new();
ctrl = $controller('TestCtrl', {
$scope: scope
});
CrudService = _CrudService_;
backend = $httpBackend;
}));
it('should defined the service getGroups', function () {
expect(CrudService.getGroups).toBeDefined();
});
//this is wrong!
it('should returns a successful response', function () {
backend.expectGET('http://localhost:63831/api/group').respond(200, 'success');
backend.flush();
});
});
I don't know how to get a response in the test. I'm new in unit testing and need some help.
For a better comprehension here is the service code:
//CrudService file:
...
return {
getAllGroups: function () {
return ResService.group.query();
}
}
...
//ResService file:
return {
group: $resource(baseUrl + '/api/group/:Id', {
Id: '#Id'
}, {})
}
Do anyone has an idea?
It's incorrect in the sense that it's not a unit test. If you are testing controller here, then you should mock CrudService and test that $scope.groups has been assigned correctly.
beforeEach(function () {
module(function ($provide) {
$provide.factory('CrudService', function () {
return {
getAllGroups: function () {
return {
$promise: null // return an actual promise here
}
}
}
});
});
});
it('should set groups', function () {
expect($scope.groups).toEqual('success')
});
And you need a separate spec to test if CrudService calling backend correctly.
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?
I have an spec that test's if the method in scope was called (see below)
describe("Event Module tests", function () {
var scope, simpleController;
beforeEach(module('SimpleApplication'));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
simpleController = $controller("SimpleController", {
$scope: scope
});
}));
it("Scope function should be triggered", function () {
spyOn(scope, "trigger");
scope.trigger();//invoke the function on controller
expect(scope.trigger).toHaveBeenCalled();//Passes
expect(scope.isTriggered).toBeTruthy();//Fails
});
});
Application Code(Code to be tested):
angular
.module("SimpleApplication", [])
.controller("SimpleController", function ($scope) {
$scope.message = "Hello World";
$scope.isTriggered = false;
$scope.trigger = function() {
$scope.isTriggered = true;
};
});
Jasmine reports that "Expected false to be truthy.". How come ? since the method sets it to true !!
Update:
For some reason, SpyOn was mutating my object to something it was intended for. So below piece of code works good
it("Scope function should be triggered", function () {
scope.trigger();//invoke the function on controller
expect(scope.isTriggered).toBeTruthy();//Now Passes
});
spyOn doesn't call your method. It just spies. If you want it to be called you have to add something:
spyOn(scope, "trigger").andCallThrough()