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.
Related
Note, related How to test if jQuery 3.0 beta is Promises/A+ compatible in browser?
For example, at promises-tests one of the tests is found at promises-tests/lib/tests/2.1.2.js
"use strict";
var assert = require("assert");
var testFulfilled = require("./helpers/testThreeCases").testFulfilled;
var adapter = global.adapter;
var deferred = adapter.deferred;
var dummy = { dummy: "dummy" }; // we fulfill or reject with this when we don't intend to test against it
describe("2.1.2.1: When fulfilled, a promise: must not transition to any other state.", function () {
testFulfilled(dummy, function (promise, done) {
var onFulfilledCalled = false;
promise.then(function onFulfilled() {
onFulfilledCalled = true;
}, function onRejected() {
assert.strictEqual(onFulfilledCalled, false);
done();
});
setTimeout(done, 100);
});
specify("trying to fulfill then immediately reject", function (done) {
var d = deferred();
var onFulfilledCalled = false;
d.promise.then(function onFulfilled() {
onFulfilledCalled = true;
}, function onRejected() {
assert.strictEqual(onFulfilledCalled, false);
done();
});
d.resolve(dummy);
d.reject(dummy);
setTimeout(done, 100);
});
specify("trying to fulfill then reject, delayed", function (done) {
var d = deferred();
var onFulfilledCalled = false;
d.promise.then(function onFulfilled() {
onFulfilledCalled = true;
}, function onRejected() {
assert.strictEqual(onFulfilledCalled, false);
done();
});
setTimeout(function () {
d.resolve(dummy);
d.reject(dummy);
}, 50);
setTimeout(done, 100);
});
specify("trying to fulfill immediately then reject delayed", function (done) {
var d = deferred();
var onFulfilledCalled = false;
d.promise.then(function onFulfilled() {
onFulfilledCalled = true;
}, function onRejected() {
assert.strictEqual(onFulfilledCalled, false);
done();
});
d.resolve(dummy);
setTimeout(function () {
d.reject(dummy);
}, 50);
setTimeout(done, 100);
});
});
Requirement:
The ability to run the test in the browser without reliance on node.js , a server, or installing libraries?
Question:
How can this test be converted to a version using native methods available at browsers, for example Console methods; that is, substitution of console.assert() or other native method available at window for describe() and specify()?
I still think this is a terrible idea, but here's a (trivial) example:
var assert = {};
assert.equals = function(expected, value, msg) {
var message = msg || "Test ";
try {
expected == value;
console.log(message, "passed");
catch (e) {
console.log(message, "failed", e);
}
};
Now repeat for >, <, ===, ranges, exceptions when they're expected, specific exceptions (e.g. TypeError, SyntaxError), typeof, instanceof, other types (built-ins, constructed), problematic values like NaN and null, etc.
NOTE: console.assert
There are some problems with console.assert. First, its non-standard and may not behave identically across platforms. Second, there is, AFAIK, no good way to abstract it: you'd end up doing just as much work as the above solution using console.log unless you use eval and string arguments:
function assert(str, msg) {
try {
console.assert(eval(str), msg);
catch (e) {
console.log(msg, " failed:", e);
}
}
assert("3 === 4", "Three equals four"); // logs the assertion failure.
Needless to say, I do not recommend this, as manually constructing the strings is error-prone, eval (even though safe in this case) is a notorious perf killer, and not using eval means using a parser and bam we're right back to the library bit.
Seriously, as you go down this road you will think of more and more things you want to be in there (see my list above) and you will realize you are writing a library when you could have used one.
UPDATE
Per your question in the comments below, many testing libraries (e.g. mocha, jasmine) use a format like this:
wrapper('name of the thing being tested', function(done) {
innerWrapper('description of the test', function(done) {
assert(someAssertion);
});
innerWrapper('some other test for that function', function(done) {
assert(somethingElse);
someAsyncFunction().then(function(value) {
assert(somethingAboutValue);
done();
});
});
});
The 'wrapper', 'innerWrapper', and 'assert' are added by including the testing library in the code or running the command (for cli) i.e. 'mocha tests.js' instead of 'node tests.js'. The setup may or may not use the inner functions to specify subtests. 'Done' is an argument to the callback that can be used to signal the end of an async test.
QUnit is a little simpler in its API, but not too far off.
UPDATE 2
Those for the most part are names for the same things: functions that wrap a test condition to make sure that correct messages are logged, or exceptions caught, or async gets a chance to finish. The assert tests the actual condition to be evaluated. The adapter reference means wrapping the jQuery deferred constructor to match the API in the spec.
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();
});
});
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