I'm writing a test in Mocha / Node js and want to use a setTimeout to wait for a period before executing a block of code.
How can I accomplish this?
It appears that within a Mocha test case, setTimeout() does not work. (I am aware that you can setTimeout per test case and per test file, that's not what I need here.)
In a js file run with Node, ie, node miniTest.js, this will wait 3 seconds, then print the line inside the setTimeout function.
miniTest.js
console.log('waiting 3 seconds...');
setTimeout(function() {
console.log('waiting over.');
}, 3000);
In a js file run with Mocha, ie, mocha smallTest.js, this will not wait, and will finish executing and exit without ever printing the line inside the setTimeout function.
smallTest.js:
mocha = require('mocha');
describe('small test', function() {
it('tiny test case', function() {
console.log('waiting 3 seconds...');
setTimeout(function () {
console.log('waiting over.')
}, 3000);
});
});
You are forgetting to pass parameter in it('tiny test case', function() and call done() after console.log in setTimeout method.
describe('small test', function(){
it('tiny test case', function(done){
console.log('waiting 3 seconds');
setTimeout(function(){
console.log('waiting over.');
done();
}, 3000)
})
})
You need to have done passed as a parameter in your test which will be invoked once the test passes.
You can write your test like
mocha = require('mocha');
describe('small test', function(done) {
it('tiny test case', function() {
console.log('waiting 3 seconds...');
setTimeout(function () {
console.log('waiting over.');
done();
}, 3000);
});
});
This will wait 3 seconds after that it will print 'waiting over' and pass the test. You can also have a condition inside the timeout depending upon whether the condition is satisfied or not you can pass the test by calling
done();
or fail the test by either throwing an error or passing the error message in
done("Test Failed");
Have your test function take a parameter (typically called done). Mocha will pass a function that you'll call in the setTimeout function (e.g. after console.log call done()).
See https://mochajs.org/#asynchronous-code.
This is a complete example. You need to call done() in every assertion you make. You can leave out the before function and do the setTimeout in one of your it functions, but it should still use and call done() after assert.
var foo = 1;
before(function(done) {
setTimeout(function(){
foo = 2;
done();
}, 500)
});
describe("Async setup", function(done){
it("should have foo equal to 2.", function(done){
expect(foo).to.be.equal(2);
done();
});
it("should have foo not equal 3.", function(done){
expect(foo).to.be.not.equal(3);
done();
});
});
Related
I have the problem:
var async = require('async');
function a() {
async.series([b,c], function(err) {
console.log('Done');
});
};
function b(next) {
next();
};
function c(next) {
next();
};
var methods = {
a: a,
b: b,
c: c
};
And I am trying to write a test like so:
spyOn(methods.a);
methods.a();
expect(methods.b).toHaveBeenCalled();
expect(methods.c).toHaveBeenCalled();
However both b and c do not register as having been called. Any ideas how to properly test this behavior?
If you use spy on function or method then Jasmine will be look at this function for check call it. And called function under Jasmine Spy is not execute its code by default.
spyOn(methods, "a");
methods.a();
expect(methods.a).toHaveBeenCalled();
In your case, you need check async code execution. We can do it with done function:
it("should support async execution", function(done) {
var MAX_ASYNC_DELAY = 2000;
spyOn(methods, "b");
spyOn(methods, "с");
methods.a();
setTimeout(function(){
expect(methods.b).toHaveBeenCalled();
expect(methods.c).toHaveBeenCalled();
done();
}, MAX_ASYNC_DELAY );
});
It will work if in methods.a() you will use next:
function a() {
async.series([methods.b, methods.c], function(err) {
console.log('Done');
});
}
If such adjustment is not possible, you should rewrite the test case in the following:
it("should support async execution", function(done) {
var MAX_ASYNC_DELAY = 2000;
spyOn(window, "b");
spyOn(window, "с");
methods.a();
setTimeout(function(){
expect(b).toHaveBeenCalled();
expect(c).toHaveBeenCalled();
done();
}, MAX_ASYNC_DELAY );
});
Owen Ayres advises not to use setTimeout in the test case. But this is impossible in your case if you are using Jasmine. Because jasmine.DEFAULT_TIMEOUT_INTERVAL is timeout waiting for call done function.
Example, your async timeout is eqaul nearly 10000 ms and you set MAX_ASYNC_DELAY to 11000 ms. The test case will be marked as failed, because jasmine.DEFAULT_TIMEOUT_INTERVAL equal 5000 ms by default. That need use override this param:
var originalTimeout;
beforeEach(function() {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 12000; // ms to wait for done()
});
it("should support async execution", function(done) {
var MAX_ASYNC_DELAY = 11000;
// test case from above
});
afterEach(function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
if you use Jasmine version 2.2 and above you can write:
it("should support async execution", function(done) {
var MAX_ASYNC_DELAY = 11000;
// test case from above
}, 12000);
Avoid setTimeout in a unit test at all costs. Here is how you should test it in a readable, 'Triple A Testing' format. Using Jasmine's async done will ensure it waits for a certain length of time before failing if the function call is never made. If you want to customise this for this test case, you could do something like defined in my beforeEach below (but inside the test itself).
beforeEach(function() {
// these are not required, including to show you can have if desired
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; // ms to wait for done()
});
it('calls methods b and c when a is called', function (done) {
var a = spyOn(methods.a);
var b = spyOn(methods.b);
methods.a();
expect(a).toHaveBeenCalled();
expect(b).toHaveBeenCalled();
done();
});
afterEach(function() {
// could be inline of above test if not needed for multiple cases.
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
Do not use timeouts in the test. This is not a clean way of testing and should be avoided unless absolutely essential.
I'm trying to use Jasmine to test asynchronous methods.
I have something like this:
function change(obj) {
setTimeout(function() { obj.value = 1 }, 500);
}
In my test:
it('should change the value', function(done) {
....
obj.value = 0;
change(obj);
done();
obj.value.should.equal(1)
}
How can I get it work?
UPDATE: It works like this now, but it messes with some other tests that worked before, should I reset something after the test?
You can call done using setTimeout
setTimeout(function(){
done();
}, 1000);
I'm trying to write a plugin for Jasmine that allows you to return a promise from a spec and will pass or fail that spec depending on whether or not the promise is fulfilled or rejected.
Of course, I want to write tests to make sure that my plugin works correctly, and to be thorough, I need to make sure that tests fail when the promise is rejected... so how do I make a test pass when I need to make sure that a test "successfully fails"?
After a conversation with the developers who work on Jasmine, we've come up with this:
var FAILED = 'failed'
var PASSED = 'passed'
describe('My Test Suite', function () {
var env
beforeEach(function () {
// Create a secondary Jasmine environment to run your sub-specs in
env = new jasmine.Env()
})
it('should work synchronously', function () {
var spec
// use the methods on `env` rather than the global ones for sub-specs
// (describe, it, expect, beforeEach, etc)
env.describe('faux suite', function () {
spec = env.it('faux test', function (done) {
env.expect(true).toBe(true)
})
})
// this will fire off the specs in the secondary environment
env.execute()
// put your expectations here if the sub-spec is synchronous
// `spec.result` has the status information we need
expect(spec.result.status).toBe(FAILED)
})
// don't forget the `done` argument for asynchronous specs
it('should work asynchronously', function (done) {
var spec
// use the methods on `env` rather than the global ones.
env.describe('faux suite', function () {
// `it` returns a spec object that we can use later
spec = env.it('faux test', function (done) {
Promise.reject("FAIL").then(done)
})
})
// this allows us to run code after we know the spec has finished
env.addReporter({jasmineDone: function() {
// put your expectations in here if the sub-spec is asynchronous
// `spec.result` has the status information we need
expect(spec.result.status).toBe(FAILED)
// this is how Jasmine knows you've completed something asynchronous
// you need to add it as an argument to the main `it` call above
done()
}})
// this will fire off the specs in the secondary environment
env.execute()
})
})
Going off Joe's answer, I moved the fake test context into a single function. Since the code under test is making use of jasmine expectations, I load the inner Env into jasmine.currentEnv_ and call it explicitly with jasmine.currentEnv_.expect(). Note that currentEnv_ is an internal variable set by jasmine itself, so I can't guarantee that this won't be broken in a future jasmine version.
function internalTest(testFunc) {
var outerEnvironment = jasmine.currentEnv_;
var env = new jasmine.Env();
jasmine.currentEnv_ = env;
var spec;
env.describe("fake suite", function () {
spec = env.it("fake test", function () {
func();
});
});
env.execute();
jasmine.currentEnv_ = outerEnvironment;
return spec.result;
}
Then each test looks like
it("does something", function () {
//Arrange
//Act
var result = internalTest(function () {
//Perform action
});
//Assert
expect(result.status).toBe("failed"); //Or "success"
expect(result.failedExpectations.length).toBe(1);
expect(result.failedExpectations[0].message).toBe("My expected error message");
});
I was trying to put together a small example to show co-workers but can't figure out what's wrong with this test that I've put in a gist.
Essentially I want to test a function that does something async, but use Sinon's spy() functionality to assure it completes:
function asyncHello(name, delay, cb) {
setTimeout(function() {
console.log("running after ", delay);
cb("hello " + name);
}, delay);
}
suite('Mega Suite', function(){
suite("testing async hello", function() {
test('should call the callback', function(done) {
var cb = sinon.spy();
asyncHello("foo", cb);
cb.should.have.been.called();
done();
});
});
});
Thought using Mocha and done() to resolve a test that depends on an async function (setTimeout, in this case) would work, but maybe someone can point out where I'm wrong. Thanks!
You don't need Sinon for this:
function asyncHello(name, delay, cb) {
setTimeout(function() {
console.log("running after ", delay);
cb("hello " + name);
}, delay);
}
suite('Mega Suite', function(){
suite("testing async hello", function() {
test('should call the callback', function(done) {
asyncHello("foo", 1000, function () {
done();
});
});
});
});
There were two problems in this code:
You called asyncHello("foo", cb); which made it so that your delay argument inside the function was set to cb and the cb argument inside the function was undefined.
Even after fixing the 1st item cb.should.have.been.called(); was called before the function passed to setTimeout could execute.
You basically don't need to use Sinon because if you just set a callback to call done() then you know the test is successful. If there's a problem anywhere done() won't get called and the test will fail.
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