Good day!
This is my first time writing a unit test and I don't even know where to begin. I have look through some example and here is my code.
This is a validation function located in my directive
scope.validateInput = function () {
if (scope.description < 100) {
scope.validate_description_message = true;
}
}
Here is my unit test
describe('scope.validateInput', function() {
beforeEach(inject(function ($rootScope, $compile) {
scope = $rootScope;
element = angular.element(html);
$compile(element)(scope);
scope.$digest();
}));
it('should return true when scope.description is less than 100 characters.', inject(function() {
$rootScope.description = 1;
$rootScope.validateInput();
expect($rootScope.validate_description_message).toEqual(true);
}));
});
Thank you in advance!
You should declare your $scope inside the describe first. And create a new scope from the $rootscope.
describe('scope.validateInput', function() {
var scope;
beforeEach(inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
element = angular.element(html);
$compile(element)(scope);
scope.$digest();
}));
it('should return true when scope.description is less than 100 characters.', inject(function() {
scope.description = 1;
scope.validateInput();
expect(scope.validate_description_message).toEqual(true);
}));
});
Related
I am new to unit test.
Please help me to write test case code for following code:
$scope.convertToInt = function (str) {
if (!isNumberEmpty(str) && !isNaN(str)) {
return parseInt(str, 10);
}
return "";
}
I have tried like this, but not able to do.
describe('ConfigurationTestController', function() {
beforeEach(module('usp.configuration'));
describe('ConfigurationController', function () {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('ConfigurationController', {
'$scope': scope
});
}));
});
});
Please tell me how can I write.......
you need to modify your code a bit. you don't need to use describe twice in your test case.
(function() {
"use strict";
describe("test suite for Configuration test controller", function() {
var scope = null;
beforeEach(module("usp.configuration"));
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
$controller("ConfigurationController", {
$scope: scope
});
}));
it("should convert to int", function() {
expect(scope.convertToInt("2")).to.equal(2);
});
it("should return empty string", function() {
expect(scope.convertToInt("asd")).to.equal("");
});
});
}());
So I am trying to learn how to unit test with Jasmine with Angular. I have got a number of unit tests working but this one has stumped me. I took out the alerts array in my test you can make it any array.. But how to mock this and getting this to work has really stumped me. I would think that the object would exist.
(function () {
var app = angular.module("TabDirectives", ["Communication"]);
app.directive("sentAlerts", ["DirectiveProvider", "DataProvider", function (DirectiveProvider, DataProvider) {
var dir = DirectiveProvider("SentAlerts");
dir.controller = function () {
var ctrl = this;
DataProvider("AlertsByDate").then(function (Result) {
ctrl.AlertList = Result.data;
});
};
dir.controllerAs = "Alerts"
return dir;
}]);
})()
I have a test that looks like
describe("Tab Directive Unit Tests", function () {
var controller;
describe("Tab Directive Default Values", function () {
beforeEach(inject(function ($rootScope, $compile) {
element = angular.element("<sent-alerts></sent-alerts>");
$compile(element)($rootScope.$new());
$rootScope.$digest();
controller = element.controller;
}));
it("Ctrl should be this", function () {
expect(controller.ctrl).toBe(controller.this);
});
it("AlertList should have Alerts", function () {
expect(controller.ctrl.AlertList).toBe(alerts);
});
});
});
The error I'm getting looks like
TypeError: Cannot read property 'AlertList' of undefined
You have to initialize and inject your controller as well. Something like this:
var $controller;
var $rootScope;
var scope;
var controller;
beforeEach(inject(function (_$controller_, _$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
controller = $controller('ScopeController', { '$scope': scope });
}));
I have a directive that look like this:
angular.module('myApp', [])
.directive('myDirective', ['$window','MyConfig', function($window,MyConfig){
return {
link: function(scope, element, attr, controller) {
var w = angular.element($window);
function adjustTop(){
var oldtop = scope.top;
if(oldtop<15){
scope.top += 2;
}else{
scope.top = 0;
}
}
if(MyConfig.adjusttop){
w.bind('scroll', function () {
adjustTop();
});
};
}
};
}]);
How can I spyOn to detect adjustTop() function have been called when MyConfig.adjusttop is true?
You shouldn't do that. Testing private members arguably isn't a good idea because it couples your tests to a specific implementation, making them fragile.
In general you should test what your code does, not how it does it. In this case, you could write tests to ensure the scope.top property is correctly updated after the scroll event has been triggered.
Updated to address OP's comment:
Here's a way for you to test what you want:
describe('my test', function() {
var $scope, $window, $compile, MyConfigMock;
beforeEach(function() {
module('plunker');
MyConfigMock = {};
module(function($provide) {
$provide.value('MyConfig', MyConfigMock);
});
inject(function($rootScope, _$window_, _$compile_) {
$scope = $rootScope;
$window = _$window_;
$compile = _$compile_;
});
});
it('updates top on scroll if set to do so', function() {
// Arrange
MyConfigMock.adjusttop = true;
$scope.top = 15;
$compile('<my-directive></my-directive>')($scope);
// Act
$($window).trigger('scroll');
// Assert
expect($scope.top).toBe(0);
});
});
Working Plunker
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);
}
}
}
We have few methods in Angular Controller, which are not on the scope variable.
Does anyone know, how we can execute or call those methods inside Jasmine tests?
Here is the main code.
var testController = TestModule.controller('testController', function($scope, testService)
{
function handleSuccessOfAPI(data) {
if (angular.isObject(data))
{
$scope.testData = data;
}
}
function handleFailureOfAPI(status) {
console.log("handleFailureOfAPIexecuted :: status :: "+status);
}
// this is controller initialize function.
function init() {
$scope.testData = null;
// partial URL
$scope.strPartialTestURL = "partials/testView.html;
// send test http request
testService.getTestDataFromServer('testURI', handleSuccessOfAPI, handleFailureOfAPI);
}
init();
}
Now in my jasmine test, we are passing "handleSuccessOfAPI" and "handleFailureOfAPI" method, but these are undefined.
Here is jasmine test code.
describe('Unit Test :: Test Controller', function() {
var scope;
var testController;
var httpBackend;
var testService;
beforeEach( function() {
module('test-angular-angular');
inject(function($httpBackend, _testService_, $controller, $rootScope) {
httpBackend = $httpBackend;
testService= _testService_;
scope = $rootScope.$new();
testController= $controller('testController', { $scope: scope, testService: testService});
});
});
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('Test controller data', function (){
var URL = 'test server url';
// set up some data for the http call to return and test later.
var returnData = { excited: true };
// create expectation
httpBackend.expectGET(URL ).respond(200, returnData);
// make the call.
testService.getTestDataFromServer(URL , handleSuccessOfAPI, handleFailureOfAPI);
$scope.$apply(function() {
$scope.runTest();
});
// flush the backend to "execute" the request to do the expectedGET assertion.
httpBackend.flush();
// check the result.
// (after Angular 1.2.5: be sure to use `toEqual` and not `toBe`
// as the object will be a copy and not the same instance.)
expect(scope.testData ).not.toBe(null);
});
});
I know this is an old case but here is the solution I am using.
Use the 'this' of your controller
.controller('newController',['$scope',function($scope){
var $this = this;
$this.testMe = function(val){
$scope.myVal = parseInt(val)+1;
}
}]);
Here is the test:
describe('newDir', function(){
var svc,
$rootScope,
$scope,
$controller,
ctrl;
beforeEach(function () {
module('myMod');
});
beforeEach(function () {
inject(function ( _$controller_,_$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
$compile = _$compile_;
$scope = $rootScope.$new();
ctrl = $controller('newController', {'$rootScope': $rootScope, '$scope': $scope });
});
});
it('testMe inc number', function() {
ctrl.testMe(10)
expect($scope.myVal).toEqual(11);
});
});
Full Code Example
As is you won't have access to those functions. When you define a named JS function it's the same as if you were to say
var handleSuccessOfAPI = function(){};
In which case it would be pretty clear to see that the var is only in the scope within the block and there is no external reference to it from the wrapping controller.
Any function which could be called discretely (and therefore tested) will be available on the $scope of the controller.