Testing that an $interval gets fired 3 times in AngularJS - javascript

I have an AngularJS service that looks like this (I've removed the unrelevant code):
myApp.factory('myService', ['$interval', function ($interval) {
return {
startWatch: function(onSuccess) {
var watcher = $interval(onSuccess, 250);
return watcher;
},
stopWatch: function(watcher) {
$interval.cancel(watcher);
}
};
}]);
To test this service, I have the following:
describe('myApp', function() {
beforeEach(function() {
module('myApp');
});
describe('myService', function () {
var myService = null;
beforeEach(inject(function (myService) {
myService = myService;
}));
it('should run successfully 3 times', function() {
var count = 0;
var w = myService.startWatch(function() {
count = count + 1;
console.log('fired ' + count + ' times.');
});
});
});
});
When the test gets ran, it just races through it one time. I do not understand how to actually test 1. the interval piece 2. the number of times it gets fired. There will be information in the service that changes over time. I want to test that the information is behaving as expected. For that reason, I'm trying to test a number of things over time. How do I do this?
Thank you for your help!

Try $interval.flush(millis):
describe('myApp', function() {
beforeEach(function() {
module('myApp');
});
describe('myService', function () {
var myService = null;
var $interval = null;
beforeEach(inject(function (myService) {
myService = myService;
$interval = _$interval_;
}));
it('should run successfully 3 times', function() {
var count = 0;
var w = myService.startWatch(function() {
count = count + 1;
});
$interval.flush(751); // greater than 250 * 3
expect(count).toEqual(3);
});
});
});
Remember to reference angular mock to your test: http://code.angularjs.org/1.2.0/angular-mocks.js .The version should be the same with your angular version.

Related

Mock a function present in angularJS controller using jest - testing

I've a small controller written in angularJS.
1st function is actually calling a 2nd one to perform a calculation and return it back.
I want to mock the 2nd function in my testing, so that it returns value I've provided in mock instead of calling the function.
ABCService.js
var app = angular.module('mathModule', []);
app.controller('mathService', ['$scope', function($scope){
$scope.first = 0;
$scope.second = 0;
$scope.addTwoNumbers = function(x, y) {
return x + y;
};
$scope.callAddFunction = function() {
return $scope.addTwoNumbers(10, 20);
}
}]);
ABCServic.test.js
require('./mathService.js');
describe('Math service', function() {
beforeEach(
angular.mock.module('mathModule')
);
var $controller;
beforeEach(inject(function(_$controller_) {
$controller = _$controller_;
}));
describe('Test using 2 numbers', function() {
var $scope, controller;
beforeEach(function() {
$scope = {};
controller = $controller('mathService', { $scope: $scope });
});
it("Nested function", function() {
var total = $scope.callAddFunction();
expect(total).toEqual(31);
});
});
});
Here I want to mock addTwoNumbers() so that instead of calling we get value we've provided during testing.
Something like, Mock(addTwoNumbers(x,y)) = 0, so now callAddFunction will return 0 instead of 30, which it should be returning if not mocked.

bcbankApp.accsummary module AccountSummaryController should have a getAccountSummary function FAILED in angular js

I am working on Banking app using Angularjs on hackerrank where I am stuck at point.I am trying to call my function from Account summary controller but It is saying that No such function exist in my controller
Here is my code
AccountSummarycontroller.js
// Create the controller AccountSummaryController with getAccountSummary function that access accountSummaryService which is already defined in account-summary.service.js. Use $state for Transition from one page to another.
(function() {
'use strict';
var appContr = angular.module('abcbankApp.accountSummary', ['abcbankApp.accountSummaryService']);
appContr.controller('AccountSummaryController', function(AccountSummaryService) {
var ActSumCtrl = this;
// this.AccountSummaryService.getAccountSummary();
ActSumCtrl.accountList = [];
ActSumCtrl.accountList = AccountSummaryService.getAccountSummary();
});
})();
AccountSumaaryService.js
// Create the service AccountSummaryService with getAccountSummary function which should return the account details from data/account-summary.json using $http.get.
(function() {
'use strict';
var appServc = angular.module('abcbankApp.accountSummaryService', []);
appServc.factory('AccountSummaryService', ['$http',
function($http) {
return {
getAccountSummary: function() {
var data;
$http.get('./data/account-summary.json')
.then(function(response) {
data = response.data;
});
return data;
}
};
}]);
})();
But I am getting error something like below
abcbankApp.accsummary module AccountSummaryController should have a getAccountSummary function FAILED.Expected false to be true.
Testfile.js
describe('AccountSummaryController', function() {
it('Controller definition', function() {
expect(AccountSummaryController).toBeDefined();
});
it('should have a getAccountSummary function', function() {
expect(angular.isFunction(AccountSummaryController.getAccountSummary)).toBe(true);
});
});
});
})();
Any Help will be Appreciated.Thanks in advance
Well to answer your question lets begin with your test case as it is showing that AccountSummaryController.getAccountSummary should be defined
So you should call your function with your controller name and function name
Here is modified code
(function() {
'use strict';
var appContr = angular.module('abcbankApp.accountSummary', ['abcbankApp.accountSummaryService']);
appContr.controller('AccountSummaryController', function(AccountSummaryService) {
var ActSumCtrl = this;
// this.AccountSummaryService.getAccountSummary();
ActSumCtrl.accountList = [];
ActSumCtrl.getAccountSummary=function()
{
//move your logic here
}
ActSumCtrl.accountList = AccountSummaryService.getAccountSummary();
});
})();
Let me know in comment weather it worked or not

How to unit test .then function with jamsine

I have written one component which post data from some service.
Am not able to cover then function in Unit Test. How to enter test `then' function?
angular.module('myapp')
.component('performAnalysis', {
templateUrl: 'analysis.component.html',
controller: PerformAnalysisController,
controllerAs: 'vm'
});
function PerformAnalysisController(UnitySizerService, SizingService, MarshallDTO, CommonService) {
let vm = this;
vm.$onInit = $onInit;
function $onInit() {
let unitySizerDTO = MarshallDTO.generateDTO();
let previousDTO = CommonService.getProperty('previousDTO');
vm.dataChanged = JSON.stringify(unitySizerDTO) === JSON.stringify(previousDTO);
/* Call Backend API only if DTO is changed */
if (!vm.dataChanged) {
/* Loader On */
vm.activateLoader = true;
SizingService.postSizingResults(unitySizerDTO).then(function (data) {
UnitySizerService.resultSummary = data;
/* New Data coming from Backend */
vm.dataChanged = true;
/* Loader Off */
vm.activateLoader = false;
CommonService.setProperty('previousDTO', unitySizerDTO);
vm.unitySizerService = UnitySizerService;
});
}
else {
vm.unitySizerService = UnitySizerService;
}
}
}
This is test file which I have written, but am not able to cover then function inside this:
describe('my Component', function () {
beforeEach(module('myApp'));
let vm;
let $rootScope;
let $componentController;
let UnitySizerService, SizingService;
beforeEach(inject(function (_$componentController_, _$rootScope_, _UnitySizerService_, _SizingService_) {
$componentController = _$componentController_;
$rootScope = _$rootScope_;
UnitySizerService = _UnitySizerService_;
SizingService = _SizingService_;
vm = $componentController('performAnalysis');
}));
it('should be defined', function () {
expect(vm).toBeDefined();
expect(vm.$onInit).toBeDefined();
expect(UnitySizerService).toBeDefined();
});
it('should show loader', function () {
vm.$onInit();
vm.dataChanged = false;
expect(vm.activateLoader).toBeTruthy();
});
});
In order to mock .then of a Promise in jasmine, you can do something like this
var deferred = $q.defer();
var whatServiceReturns = "test";
// we are returning promise from service function
SizingService.postSizingResults.and.returnValue({$promise:deferred.$promise});
// now let's call the original function
vm.$onInit();
// we will resolve our promise so that we can reach inside 'then'
// here, 'whatServiceReturns' resembles your 'data' parameter in 'then' function
deferred.resolve(whatServiceReturns);
$rootScope.$digest();
// here we can expect things like 'dataChanged' and 'activateLoader' if we see from your example
expect(...).toEqual(...);
You can use deferred.reject(someError); for error.
Edit: added comments to elaborate the code

angular watch object not in scope

I have a service in which values can change from outside Angular:
angularApp.service('WebSocketService', function() {
var serviceAlarms = [];
var iteration = 0;
this.renderMessages = function(alarms, socket) {
if (! angular.equals(serviceAlarms, alarms)) {
serviceAlarms = alarms;
iteration++;
}
};
this.getAlarms = function () {
return serviceAlarms;
};
this.iteration = function () {
return iteration;
};
this.socket = initSocketIO(this);
});
The initSocketIO function makes callbacks to this services renderMessages() function and serviceAlarms variable gets changed on a steady basis.
Now i am trying to watch for changes in this service like so:
controllers.controller('overviewController', ['$scope', 'WebSocketService', function ($scope, WebSocketService) {
$scope.$watch(
function () {
return WebSocketService.iteration();
},
function(newValue, oldValue) {
$scope.alarms = WebSocketService.getAlarms();
},
true
);
}]);
to no avail. The second function provided to $watch never gets executed except on controller initialization.
I have tried with and without true as third parameter.
You should use $rootScope.$watch (not $scope.$watch)
I ended up using the solution below since $watch didn't work as excpected.
I refactored the solution to use $rootScope in combination with:
angularApp.run(['$rootScope', function($rootScope){
$rootScope.socket = {};
$rootScope.socket.alarms = [];
$rootScope.socket.faults = [];
$rootScope.socket.renderErrors = function(faults, socket) {
var faultArray = [];
angular.forEach(faults, function(error) {
error.value ? faultArray.push(error) : null;
});
if (! angular.equals($rootScope.socket.faults, faultArray)) {
$rootScope.socket.faults = faultArray;
$rootScope.apply();
}
};
$rootScope.socket.renderMessages = function(alarms, socket) {
if (! angular.equals($rootScope.socket.alarms, alarms)) {
$rootScope.socket.alarms = alarms;
$rootScope.$apply();
}
};
$rootScope.socket.socket = initSocketIO($rootScope.socket);
}]);
Now i have my socket-updated-model in all scopes to use freely in controllers and views.
Controller example:
$scope.acknowledgeAlarm = function(alarm) {
$scope.socket.socket.emit('acknowledgeAlarm', {
hash:alarm.icon.hash,
id:alarm.id
});
};
View example:
<div ng-repeat="alarm in socket.alarms">
{{alarm.name}} {{alarm.icon.progress}}
</div>

How to run basic test using karma (testacular)

I've been playing with jasmine recently in order to start incorporating tests in my projects.
All seemed to work fine until, I wanted to automate the workflow with karma (previously Karma).
In my src directory, I have simple Calculator Object with a couple of simple methods:
function Calculator() { };
var current = 0;
Calculator.prototype.add = function() {
if (arguments.length < 2) {
// we only have one arguments
current += arguments[0];
return current;
} else {
// more than one arguments
for( var i in arguments )
current += arguments[i];
return current;
}
};
Calculator.prototype.substract = function() {
var currentValue = arguments[0];
for ( var i = 1; i < arguments.length; i++ )
currentValue -= arguments[i];
return currentValue;
};
Calculator.prototype.reset = function() {
window.current = 0;
}
Then in my spec file, I do have the following ( all tests passes without Karma ):
var calculator = new Calculator();
describe('Calculator', function() {
beforeEach(function() {
window.current = 0;
});
describe('When adding numbers', function() {
it('should store the current value at all times', function() {
expect(window.current).toBeDefined();
});
it('should add numbers', function() {
expect(window.calculator.add(5)).toEqual(5);
expect(window.calculator.add(10)).toEqual(15);
});
it('should add any number of numbers', function() {
expect(calculator.add(1, 2, 3)).toEqual(6);
expect(calculator.add(1, 2)).toEqual(9);
})
});
describe('When substracting numbers', function() {
it('should substract any number of numbers', function() {
expect(calculator.substract(5, 3)).toEqual(2);
});
});
it('should reset the current value back to zero', function() {
window.current = 20;
calculator.reset();
expect(window.current).toEqual(0);
calculator.add(5);
calculator.add(20);
expect(window.current).toEqual(25);
calculator.reset();
expect(window.current).toEqual(0);
});
});
When I run karma start, I get the following:
Chrome 28.0 (Mac) ERROR
Uncaught ReferenceError: Calculator is not defined
at /Users/roland/learning/jasmine/jasmine-standalone-1.3.1/spec/calculator_spec.js:1
Thank you for the help!
It seems like you're not loading the file that has Calculator or perhaps it's being loaded after the spec file. In your Karma configuration file, you'll want to do something like this:
files = [
'path/to/calculator.js',
JASMINE,
JASMINE_ADAPTER,
'path/to/calculator_spec.js'
];

Categories

Resources