How to test the function call count of another function in Javascript? - javascript

Say I have this function:
function doSomething(n) {
for (var i = 0; i < n; i++) {
doSomethingElse();
}
}
How would I test if the doSomethingElse function is called n times??
I tried something like:
test("Testing something", function () {
var spy = sinon.spy(doSomethingElse);
doSomething(12);
equal(spy.callCount, 12, "doSomethingElse is called 12 times");
});
but this does not seem to work, because you have to call the spy while the doSomething() calls the original doSomethingElse(). How can I make this work with QUnit/sinon.js?
EDIT
Maybe it isn't even a good idea? Does this fall outside the 'unit testing' because another function is called?

You could do something like this:
test('example1', function () {
var originalDoSomethingElse = doSomethingElse;
doSomethingElse = sinon.spy(doSomethingElse);
doSomething(12);
strictEqual(doSomethingElse.callCount, 12);
doSomethingElse = originalDoSomethingElse;
});
For example: JSFiddle.

function doSomething(n) {
for (var i = 0; i < n; i++) {
doSomethingElse();
}
}
you cant spy on doSomethingElse.
doSomethingElse is not testable ,when something is not testable it needs to be refactored.
You either need to inject doSomethingElse in doSomething
OR
use a pointer:
pointer={doSomethingElse:function(){}};
function doSomething(n) {
for (var i = 0; i < n; i++) {
pointer.doSomethingElse();
}
}

Declare a global variable named count and assign it 0
window.count = 0;
Now, inside the doSomethingElse() function, increment it like count++
So, whenever you access count variable, it will return the number of times the doSomethingElse() is called.
Full code might be:
window.count = 0;
function doSomething(n) {
for (var i = 0; i < n; i++) {
doSomethingElse();
}
}
function doSomethingElse() {
count++;
// do something here
}
doSomething(22);
alert(count);// alerts 22
Or even better, call count++ whenever the function you want to be tested is called in code.
Demo: http://jsfiddle.net/583ZJ/
Note: If you want to remove it, then just remove the variable declaration (window.count=0;) and count++

function debugCalls(f) {
if (!f.count)
f.count = 0;
f.count++;
}
function doSomethingElse()
{
debugCalls(arguments.callee);
// function code...
}
// usage
for(var i = 0; i < 100; i++) doSomethingElse();
alert(doSomethingElse.count);
this way it makes it easier for you to debug any function you want just by inserting debugCalls(arguments.callee) inside the function you want to save the number of times it has been called.

In Node.js 14.2.0 one can use the new currently experimental CallTracker API to do the job without using Sinon or another additional library.
var assert = require('assert');
test("Testing something", function () {
var originalDoSomethingElse = doSomethingElse;
var tracker = new assert.CallTracker();
doSomethingElse = tracker.calls(doSomethingElse, 12);
try {
doSomething(12);
tracker.verify();
} finally {
doSomethingElse = originalDoSomethingElse;
}
});

Related

Passing variable into named callback

OK, let's keep it simple, I have the following :
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
dust.render('product', product, addProductOrFinish);
}
}
and i'd like to get the current value of i inside my callback function
function addProductOrFinish(err, out) {
console.log(i); // undefined
}
I do know it's simple, really I do... Help?
Edit :
I know I'm supposed to use a closure so I tried and failed with this :
(function(i){
dust.render('product', product, addProductOrFinish);
};(i)
There are several different structures that could be used to solve this problem. The simplest is to use .bind() to add the desired parameter to the function call.
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
dust.render('product', product, addProductOrFinish.bind(null, i));
}
}
function addProductOrFinish(i, err, out) {
console.log(i);
}
This will cause the value of this to change in addProductOrFinish if that was important. If so, you could work around that too, but it's not as simple.
Here's another approach using a closure that returns a function and preserves the value of this in case dust.render() is setting that:
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
dust.render('product', product, getAddProductOrFinish(i));
}
}
function getAddProductOrFinish(loopArg) {
return function(err, out) {
return addProductOrFinish.call(this, loopArg, err, out);
}
}
function addProductOrFinish(i, err, out) {
console.log(i);
}
Or, if addProductOrFinish can be an inline function, then it can use the IIFE-type structure you tried like this:
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
(function(i) {
dust.render('product', product, function(err, out) {
console.log(i);
// rest of your addProductOrFinish logic here
});
)(i);
}
}

Is there a better way to do callback chaining in javascript?

I wrote a callback helper, that lets me group multiple callbacks into one function variable:
function chainCallbacks() {
var callbacks = arguments;
return function () {
for(var i = 0; i < callbacks.length; i++) {
if(callbacks[i] != null) {
callbacks[i].apply(null, arguments);
}
}
};
}
this works, but I'm wondering if there are any javascript libraries that provide the same functionality? or even better, something that simulates the .NET "event" pattern?
myEvent+=myCallback;
I have modified your chainCallbacks function. You can test below code in JS console (I'm using Chrome -works fine), and check the result.
var result = 0;
function a() {
result += 5;
console.log(result);
_next();
}
function b() {
result += 10;
console.log(result);
_next();
}
function c() {
result += 20;
console.log(result);
_next();
}
function chainCallbacks() {
var _this = this;
var _counter = 0;
var _callbacks = arguments;
var _next = function() {
_counter++;
if(_counter < _callbacks.length) {
_callbacks[_counter].apply(_this);
}
};
_this._next = _next;
return function() {
if(_callbacks.length > 0) {
_callbacks[0].apply(_this);
}
};
}
var queue = chainCallbacks(a, b, c);
queue();
Idea is simple - you call _next() whenever your callback function has finished executing, and you want to jump to another. So you can call _next() e.g. after some jQuery animation as well, and this way you will preserve the order of the functions.
If you want to replace a callback with one that calls the original as well as some others, I'd probably just do something like this:
Requirejs.config.callback = function(orig) {
var fns = [orig, first, second, third];
return function() {
fns.forEach(function(fn) { fn.apply(null, this); }, arguments);
};
}(Requirejs.config.callback);
But if you're doing this often, I think your solution will be as good as it gets. I don't see need for a library.
Requirejs.config.callback = chainCallbacks(Requirejs.config.callback, first, second, third)
A library can't do anything to extend language syntax in JavaScript. It's limited to what's available... no operator overloading or anything.

Something similar to jQuery when / then: deferred execution with break

I'm searching for a solution where I'm able to run different functions, but some of them need a timeout and all following functions need to wait until the previous one is finished. Every function should be able to break the complete process.
Now I thought pushing all functions to a stack and loop through them:
function foo() {
// bar() should wait as long the following is finished:
setTimeout(function(){
if ((new Date()).getSeconds() % 2) {
alert('foo');
// break loop through functions (bar is not called)
}
else {
// start next function (bar is called)
}
}, 1000);
}
function bar() {
setTimeout(function(){
alert('bar')
}, 1000);
}
var functions = new Array('foo', 'bar');
for (var i = 0, length = functions.length; i < length; i++) {
window[functions[i]]();
}
But how to include wait/break?!
Note: This should work with 2+ functions (amount of functions is changeable)
Note2: I don't want to use jQuery.
Note: I have updated my answer, see bottom of post.
Alright, let's take a look.
You're using the window[func]() method, so you should be able to store and use return values from each function.
Proof:
function a(){
return "value";
}
var ret_val = window['a']();
alert(ret_val);
Let's create a return rule:
If function returns true, continue execution flow.
If function returns false, break execution flow.
function a(){
//Do stuff
return (condition);
}
for(i = 0; i < n; i++){
var bReturn = window['function']();
if(!bReturn) break;
}
Now let's put it into practice.
function a(){
//Do stuff
return ((new Date()).getSeconds() % 2); //Continue?
}
function b(){
//Do stuff
return true; //Continue?
}
function c(){
//Do stuff
return false; //Continue?
}
function d(){
//Do stuff
return true; //Continue?
}
var functions = new Array('a', 'b', 'c', 'd');
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) break;
}
Depending on when you execute the script, eg, an even or uneven time period, it will only execute function a or execute functions a b & c. In between each function, you can go about your normal business.
Of course, the conditions probably vary from each individual function in your case.
Here's a JSFiddle example where you can see it in action.
With some small modification, you can for instance, make it so that if function a returns false, it will skip the following function and continue on to the next, or the one after that.
Changing
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) break;
}
To this
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) i++;
}
Will make it skip one function, every time a function returns false.
You can try it out here.
On a side-note, if you were looking for a waiting function that "pauses" the script, you could use this piece of code.
function pausecomp(millis){
var date = new Date();
var curDate = null;
do {
curDate = new Date();
}while(curDate-date < millis);
}
Update
After adjusting the code, it now works with setTimeout.
The idea is that you have an entry point, starting with the first function in the array, and pass along an index parameter of where you currently are in the array and then increment index with one to execute the next function.
Example | Code
function next_function(index){
if(index >= functions.length) return false;
setTimeout(function(){
window[functions[index+1]](index+1);
}, 1000);
}
function a(index){
//Do stuff
if(((new Date()).getSeconds() % 2)) return false; //Stop?
next_function(index);
}
function b(index){
//Do stuff
if(false) return false; //Stop?
next_function(index);
}
function c(index){
//Do stuff
if(true) return false; //Stop?
next_function(index);
}
function d(index){
//Do stuff
if(false) return false; //Stop?
next_function(index);
}
var functions = new Array('a', 'b', 'c', 'd');
//entry point
window[functions[0]](0);
This is exactly the scenario promises solve. In particular, the fact that promises can be broken is perfect for your situation, since a broken promise prevents the chain from continuing (just like a thrown exception in synchronous code).
Example, using the Q promise library discussed in the above-linked slides:
function fooAsync() {
return Q.delay(1000).then(function () {
if ((new Date()).getSeconds() % 2) {
alert("foo");
throw new Error("Can't go further!");
}
});
}
function barAsync() {
return Q.delay(1000).then(function () {
alert("bar");
});
}
var functions = [fooAsync, barAsync];
// This code can be more elegant using Array.prototype.reduce, but whatever.
var promiseForAll = Q.resolve();
for (var i = 0; i < functions.length; ++i) {
promiseForAll = promiseForAll.then(functions[i]);
}
// Of course in this case it simplifies to just
// promiseForAll = fooAsync().then(barAsync);
promiseForAll.then(
function () {
alert("done!");
},
function (error) {
alert("someone quit early!");
// and if you care to figure out what they said, inspect error.
}
).end();

Delegates - Construction/Execution -Time-Parameter

I have a helper function which allows me to call functions in a different context. It's pretty simple:
function delegate(that, thatMethod)
{
return function() { return thatMethod.apply(that,arguments); }
}
This is ok if I wan't evaluate the variables at execution of the function, but sometimes I want to give the delegate-function values which are fixed at construction time.
Sample:
var callbacks = new Array();
for(var i = 0; i < 5; i++)
{
callbacks.push(delegate(window, function() { alert(i) }));
}
callbacks[3]();
In this case my expected behavior is that I get an alert(3) but because i is evaluated at execution we don't.
I know there is another delegate function which looks something like:
function delegatedd( that, thatMethod )
{
if(arguments.length > 2)
{
var _params = [];
for(var n = 2; n < arguments.length; ++n)
_params.push(arguments[n]);
return function() { return thatMethod.apply(that,_params); }
}
else
return function() { return thatMethod.call(that); }
}
But that doesn't help me either because I want to mix both methods. It can be written like that (first version of delegate used):
function(foo) {
return delegate(window, function() {
alert(foo);
});
}(i)
So i is construction time and everything else execution time.
The disadvatage of this is that it looks pretty ugly. Is there a better way to do it? Can I somehow hide it in a function?
Thanks
You can use the bind function:
var callbacks = new Array();
for(var i = 0; i < 5; i++)
{
//callbacks.push(delegate(window, function() { alert(i) }));
callbacks.push(function(n) { alert(n) }.bind(window, i);
}
callbacks[3]();
But bind is not implemented on IE(don't know about IE9), for how get it to work on IE see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind#Compatibility.

How to call the javascript function dynamically

I need to call the javascript function dynamically after some delay, The function display_1, 2, ... n will be dynamically constructed. My script looks like this, but the function never gets triggered if I use the following code, but if I hardcode the function it just seems to be fine.
function display_1() {
alert(1);
}
function display_2() {
alert(2);
}
function display() {
var prefix = 'display_';
for(var i = 1; i < 3; i++) {
setTimeout(prefix.concat(i), 1000);
}
window.onload = display();
Instead of going via a string, you may as well group the functions into an array:
function display_1() {...}
function display_2() { ... }
var functions = [ display_1, display_2 ];
function display() {
for( var i = 0; i != functions.length; ++i ) {
setTimeout( functions[i], 1000 );
}
}
If you want to go further, you may even leave out the explicit function names:
var functions = [
function() { /*the function_1 implementation*/
},
function() { /*the function_2 implementation*/
}
];
you have to add the parenthesis so that the function is called:
setTimeout(prefix.concat(i)+"()", 1000);
or simply:
setTimeout(prefix + i + "()", 1000);
Besides of that please note that both functions are called pratically at the same time, because the timers started with ´setTimeout()` start at the same time.
Depending on what you're trying to do you might have a look at setInterval() or start the second timeout at the end of the display_1() function.
It should be
function display_1() {
alert(1);
}
function display_2() {
alert(2);
}
function display() {
var prefix = 'display_';
for(var i = 1; i < 3; i++) {
setTimeout(prefix.concat(i)+'()', 1000);
}
}
window.onload = display;
the string passed to setTimeout should call the function
onload should be set to a function, not its return value
setInterval('load_testimonial()',5000);//first parameter is your function or what ever the code u want to execute, and second is time in millisecond..
this will help you to execute your function for every given time.
If you really want a 1000ms delay between executing the functions, you could do something like this:
window.onload = function() {
var n = 0;
var functions = [
function() {
alert(1);
setTimeout(functions[n++], 1000);
},
function() {
alert(2);
setTimeout(functions[n++], 1000);
},
function() {
alert(3);
}
];
setTimeout(functions[n++], 1000);
};
(rewrite it in a less-repetitive nature if needed)

Categories