Jasmine: test a setTimeout function throws an error - javascript

I would like to test the error handling of a method, which schedules work with setTimeout. The error will be thrown in the scheduled part, i.e.:
function sutWithSetTimeout() {
setTimeout(function () { throw new Error("pang"); }, 1);
}
How do I test that an error is thrown and that it has the correct message?

You need to catch the function in the setTimeout and call them using expect(function(){fn();}).toThrow(e);. So you can spy on setTimeout to get the function:
spyOn(window, 'setTimeout');
sutWithSetTimeout();
var fn = window.setTimeout.mostRecentCall.args[0];
expect(fn).toThrow('pang');

Related

Loading modules JS

I'm having a synchronization and loading issues with some JS modules when the program starts. This error only shows up once at the beginning and then everything works, so it is an obvious sync problem.
The code:
//pyramid of doom
function initGame(){
initWorld(function(){
initPlayer(function(){
initBots(function(){
console.log("Game Loaded!");
update();
})
})
});
}
function initWorld(callback){
world.init(worldParams);
callback&&callback();
}
function initPlayer(callback){
player.init(scene,playerParams,world.getPhysicModel());
callback&&callback();
}
function initBots(callback){
bots.init(scene,botsParams,world.getPhysicModel());
callback&&callback();
}
function update() {
world.update(1/60);
player.update();
bots.update();
}
initGame();
The following is the error I'm getting.
Bots.js:112 Uncaught TypeError: Cannot read property 'mixer' of undefined
at Bots.update (Bots.js:112)
at update (Final.html:160)
What am I doing wrong? How can I synchronize the execution of the init functions?
(What I think that is going on is that the execution of initBots doesn't reach it end before the udpdate function starts to run.)
You can find the Bots.js module in my repository at ( 1 )
In bots.init you execute new THREE.ColladaLoader().load which looks to be asynchronous.
In its callback, you fill your _bots array (self._bots[modelLoaded] = bot;).
However, you execute bots.init() and do not wait for these asynchronous calls to complete before executing the initBots function callback. In the case of initGame execution, this callback executes update(), which in turn executes bots.update(), which tries to access this._bots[i].mixer with i index up to this._botsParams.length, i.e. a pre-defined value that does not account for how many items have been actually filled in _bots array.
Hence your error message: the array has no items yet at some indices, and trying to read a property on undefined throws an error.
Conclusion: common asynchronous issue.
You need to be passing the callbacks into the init functions. Guessing from a brief look at your bots code, they are not expecting to receive callbacks so you might be in for a rebuild.
You can't tell from the outside of an async function if it is done yet!
Equivalent to what you are doing:
let result = undefined
function takeTime () {
setTimeout(function() {
result = 'hello!'
}, 100)
}
function callback () {
console.log(result)
}
function run (callback) {
takeTime()
callback && callback()
}
run(callback) // undefined! takeTime has not finished yet
What you need to be doing:
let result = undefined
function takeTime (callback) {
setTimeout(function() {
result = 'hello!'
callback()
}, 100)
}
function callback () {
console.log(result)
}
function run (callback) {
callback && takeTime(callback)
}
run(callback) // 'hello!', the callback was only called once the takeTime function completed

Mocha does not catch undeclared variable

I am fairly new to using mocha and I ran into this bug. Please help me understand this behavior.
'use strict';
var flightMiddlewareMock = require(process.cwd() + '/test/mock/response/flightmock');
describe('Test Flights function: getTime', function(){
var mockData;
beforeEach(function(done){
mockData = flightMiddlewareMock.getValidData();
done();
}
it('getFlightDescription returns the full flight description', function(done){
contentUtils.loadContent({
'files': {
activity: '/flights/america'
},
'locality': "en_US"
}, function(err, bundle) {
var flightsMiddleware = new FlightsMiddleware(country, mockData.req, bundle);
console.log('inside content callback');
description = flightsMiddleware.getFlightDescription(mockData.results.body.items[0]);
assert.equal(description, "Boeing 777");
done();
}
});
});
Output looks like this
inside content callback
inside content callback
inside content callback
- Failure of test!
Question - I do not understand why in spite of using 'use strict' it does not complain about description not being declared.
Please note: If I do modify this to
var description = .....
it works viola! Am I missing something?
Thanks for your time!
Mocha will report the exception caused by trying to assign to an undeclared variable. If I run this:
"use strict";
it("foo", function (done) {
setTimeout(function () {
description = "foo";
done();
}, 1000);
});
I get the result:
1) foo
0 passing (1s)
1 failing
1) foo:
Uncaught ReferenceError: description is not defined
at null._onTimeout (test.js:5:21)
Now, I've used setTimeout which is a well-behaved function in that when the callback passed to it throws an exception, setTimeout does not prevent this exception from reaching the top of execution context. In other words, it does not swallow exceptions.
If you have a callback that throws an exception but this exception is swallowed by the code that called the callback, you'll get a test timeout but you won't know why because Mocha won't be able to detect the exception. It relies on the uncaughtException event, which is not emitted if the exception is swallowed.

Q then() always calls fail

Why does 'good' and 'Error is called' write the console in the below example?
My understanding is that you give then() something to run on success and something to run on fail?
var deferred = Q.defer();
function two() {
deferred.resolve();
return deferred.promise;
}
two().then(console.log('good'),console.log('Error is called'));
Q.then function can actually accept three parameters and all of them should be functions.
The success handler
The failure handler
The progress handler
When you do,
two().then(console.log('good'), console.log('Error is called'));
you are actually passing the result of executing both the console.logs to the then function. The console.log function returns undefined. So, effectively, you are doing this
var first = console.log('good'); // good
var second = console.log('Error is called'); // Error is called
console.log(first, second); // undefined, undefined
two().then(first, second); // Passing undefineds
So, you must be passing two functions to the then function. Like this
two().then(function() {
// Success handler
console.log('good');
}, function() {
// Failure handler
console.log('Error is called')
});
But, Q actually provides a convenient way to handle all the errors occur in the promises at a single point. This lets the developer not to worry much about error handling in the business logic part. That can be done with Q.fail function, like this
two()
.then(function() {
// Success handler
console.log('good');
})
.fail(function() {
// Failure handler
console.log('Error is called')
});
You have to pass functions to .then. What you did is you called console.log('good') and passed the result of calling it (which is undefined) to .then. Use it like that:
two().then(
function() { console.log('good'); },
function() { console.log('Error is called'); }
);

After setTimeout, this loses context

I have this self made function called succeeder which is supposed to try to run a function called func and if it fails to try and run it again after an interval.
This works great the first time and when func is called for the second time after setTimeout it fails and this seems out of context.
Can you think of anything that is not right in this snippet?
succeeder({
func : function () {
!this.dbOpen && this.init();
return true;
},
context : this,
interval : 2000,
success : function () { return true;}
});
function succeeder(opts) {
function run() {
try {
//_.delay(function(){opts.func();}.bind(opts.context), 2000);
setTimeout(function(){ return _.bind(opts.func, opts.context)(); }, 2000);
} catch (e) {
run(this.opts, this.interval);
}
}
run();
};
Your code doesn't do anything like the description. Also the description isn't clear: what does "if it [func] fails" mean? Does func throw an exception? Return false? Or what?
There is no code to check the return value from func, yet the example version of func returns true. What is the purpose of the true return value?
You have a try/catch block, which made me suspect you are talking about the func call throwing an exception. But this try/catch won't catch any exception that func throws!
That's because the try/catch is wrapping the setTimeout call. But func is not called from inside that try/catch block. It's called later, when the timeout fires, and the try/catch is no longer in effect at that time.
What is the opts.success function? It is never used.
Also, the code always takes a two-second delay before the first attempt to call func. Do you want that, or do you want the first call to be immediate and only take the delay if the call fails and you retry?
Here is a working example that does something like what you're talking about. I made the assumption that "fail" means "throw an exception", and that you don't want any delay on the initial func call.
To test the code I used a func function that runs a countdown and throws an exception each time and finally succeeds when the countdown reaches 0.
Run this with the debug console open so you can see the console.log() messages:
function Test() {
this.countdown = 5;
succeeder({
func: function () {
console.log(
this.constructor.name,
'countdown:',
this.countdown
);
if( this.countdown-- ) {
console.log( 'func throwing error' );
throw new Error( 'fail' );
}
console.log( 'func success!' );
},
context: this,
interval: 1000,
success: function () { return true; }
});
};
new Test;
function succeeder(opts) {
var func = _.bind( opts.func, opts.context );
function run() {
try {
func();
} catch (e) {
setTimeout( run, opts.interval );
}
}
run();
};

Expecting a timeout in QUnit

I have an asynchronous QUnit test where the test should pass if the operation times out. (I'm testing that if you omit an optional errorCallback and do something that throws an error, basically nothing happens no matter how long you wait.)
How would I do that? If I use Qunit.config.testTimeout then the test will fail on timeout. I want to set a timeout and have the test succeed when the timeout is reached.
Why not just go with a setTimeout call making the test succeed?
e.g.:
expect(1);
stop();
doOperation(function () {
start();
ok(false, "should not have come back");
});
setTimeout(function () {
start();
ok(true);
}, timeoutValue);
This is how I do in these cases (roughly):
function timeout(assert,to,error){
var done = assert.async();
var a = setTimeout(function(){
assert.equal(to,undefined,error);
done();
},to);
return function(){
done();
clearTimeout(a);
};
}
then you can:
...
var done = timeout(assert,2000,"not joined");
r.join(function(data){
assert.ok(true,"join confirmed");
done();
})
You may agument timeout function to timeout(assert,to,toCB) and execute the toCB instead of my dummy assert.equal

Categories

Resources