Somehow I'm not able to write Mocha JS test for a relatively very simple function. The JavaScript Source file looks like this
exports.cb = function() {
console.log("The function is called after 3 seconds");
}
exports.testfn = function(cb) {
setTimeout(cb, 3000);
}
And the test code is written as
describe('Main Test', function(){
it('A callback Tests', function(done){
asn.testfn(asn.cb);
done();
});
});
I'm encountering 2 problems.
The test code ends immediately with done()
If I don't call done(), then the function is called but tests fails because it expected to call done() for async functions
I've looked into documentations but not sure how this can be done.
I can write tests using promises and it works fine. But for the scenarios where we need to use setTimeout, how shall it be done?
Assuming what you're trying to test is testfn, you wouldn't use cb, you'd use a callback in the test; see comments:
describe('Main Test', function(){
it('testfn calls the function after three seconds', function(done){
// Remember the start time
var start = Date.now();
// Schedule callback
asn.testfn(function() {
// Has it been at least three seconds?
if (Date.now() - start < 3000) {
// No, trigger an error
} else {
// Yes, all's good!
done();
}
});
});
});
If you want to call asn.cb for some reason, you'd do it in the anonymous function above, but if you want to test asn.cb, you should do that serparately from testing asn.testfn.
describe('Main Test', function(){
it('A callback Tests', function(done){
asn.testfn(function() {
asn.cb();
done();
});
});
});
Related
sinon.useFakeTimers() can stub global Date constructor new Date()
Which purposes and use cases has sandbox.useFakeTimers ?
From documentation
Fakes timers and binds the clock object to the sandbox such that it too is restored when calling sandbox.restore(). Access through sandbox.clock
it still unclear how to use the second method.
new Date() in SUT still returns original time-stamp
The idea is not to replace Date; it is to avoid waiting on setTimout as it says in the docs:
Fake timers is a synchronous implementation of setTimeout and friends
that Sinon.JS can overwrite the global functions with to allow you to
more easily test code using them
Here's an example on how to use it:
var assert = require('assert');
var sinon = require('sinon');
var executed = false;
function doSomething() {
setInterval(function() {
executed = true;
}, 10000);
}
describe('doSomething', function() {
beforeEach(function() {
this.clock = sinon.useFakeTimers();
});
afterEach(function() {
this.clock = sinon.restore();
});
it('should execute without waiting on the timeout', function(){
doSomething();
this.clock.tick(10001);
assert.equal(executed, true);
});
});
In this example, the function doSomething will execute after 10000 milliseconds. Instead of waiting on that to assert the test, one could simulate time passing by using this.clock.tick(10001) and then assert that the test is passing.
I have the following files:-
target.js
var target = function(repository, logger) {
return {
addTarget : function(target) {
repository.add(target).then(
function (newTarget) {
console.log("added");
logger.info("added");
},
function (err) {
console.log("error");
logger.info("error");
}
);
}
};
};
module.exports = target;
targetTest.js
var chai = require("chai"),
expect = chai.expect,
sinon = require("sinon"),
Promise = require("bluebird"),
baseTarget = require("../target");
describe("target", function(){
it("should log error when it occurs", function() {
var mockRepository = {
add : sinon.stub().returns(Promise.reject(new Error()))
};
var mockLogger = {
info : sinon.spy()
};
var target = baseTarget(mockRepository, mockLogger);
target.addTarget("target");
expect(mockLogger.info.calledWith("error")).to.be.true;
});
});
The issue I have is that expect(mockLogger.info.calledWith("error")).to.be.true; returns false because add method on the repository is async and so hasn't executed yet. Is there a pattern for doing this properly.
This is really more of a question about 'how Promises work' than how they work within test frameworks - the answer to which is that their behaviour remains exactly the same.
Is there a pattern for doing this properly.
It is not so much a pattern as it is what Promises are built to do. Each success handler of a then is executed in sequence on success of the last. In your code we can return the Promise created by calling repository#add as you would if you wanted to use its result or perform some external dependent operation outside of addTarget:
addTarget: function (target) {
return repository
// ^^^^^^
.add(target)
.then(function (newTarget) {
console.log("added");
logger.info("added");
}, function (err) {
console.log("error");
logger.info("error");
});
}
Then place your expectation inside a then that will be executed on success of all members of the Promise chain created in addTarget:
target.addTarget("target").then(function () {
expect(mockLogger.info.calledWith("error")).to.be.true;
cb();
});
Asynchronous Tests
You will notice in the example above that there is also a call to a function cb. Due to your test being asynchronous you need to 'tell' the test framework when the test has completed. This is most often done by declaring your it function with a parameter, from which the framework will infer that the test is asynchronous and pass in a callback:
describe("target", function () {
it("should log error when it occurs", function (cb) {
// ^^^^
});
});
I am looking for a solution to test Meteor Methods with mocha. I am using Velocity and the Mocha package.
This is an example method I am trying to test.
Meteor.methods({
addPoints: function(userId, points) {
return Players.update(userId, { $inc: { score: +points } });
}
});
This is, in a round about way how I would call it using node, I want to call the methods with arguments and assert that in this case, it returns 1 for updating the mongo document
if (!(typeof MochaWeb === 'undefined')){
MochaWeb.testOnly(function(){
describe("Meteor Method: Upldating a player", function(){
// define the handler as the method we are testing
// May be this needs to be a before each.
var handler = Meteor.call("addPoints");
var userId = "1";
var points = 5;
describe("Updating a player", function() {
it("Should Add a point", function(done){
handler(userId, points, function() {
assert(handler.calledOnce);
// a way here of asserting the callback is what we expect,
// in this case we expect a return of 1
done();
});
});
});
});
});
}
Thanks
Assuming your tests run on the server, you should avoid sending a callback to the method call. This way, the Meteor method will run "synchronously" (in the fibers sense).
I would re-write the describe section as follows:
describe('Updating a player', function () {
beforeEach(function() {
handler(userId, points)
})
it('Should Add a point', function () {
assert(handler.calledOnce)
})
})
I am using a database library that its callback-based interface looks like this:
var DB = {
insert: function(options, callback) {
}
}
I want to implement a wrapper around this database to convert its callback style API to a promise based API. To do this I have defined the following class:
var DatabaseWrapper = {
init: function(db) {
this.db = db;
},
insert: function(options) {
return Q.denodeify(this.db.insert.bind(this.db))(options);
}
}
I want to write a unit test to ensure that when I call DatabaseWrapper.insert it calls DB.insert. So far my test looks like this:
describe('DatabaseWrapper', function () {
var wrapper, insertSpy, bindStub;
beforeEach(function () {
wrapper = Object.create(DatabaseWrapper);
insertSpy = sinon.spy(function () {
console.log('insertSpy got called');
});
bindStub = sinon.stub();
wrapper.db = {
insert: function (options, callback) {
}
};
sinon.stub(wrapper.db.insert, 'bind').returns(insertSpy);
});
describe('#insert', function () {
it('should delegate to db.insert', function (done) {
wrapper.insert({herp: 'derp'});
expect(wrapper.db.insert.bind).to.have.been.calledOnce;
// This fails but I expect it to succeed
expect(promise).to.have.been.calledOnce;
})
});
});
The DB instance's insert method is actually getting called as after the test fails, as the 'insertSpy got called' message is printed in the console.
But apparently it gets called after the test has failed.
As far as I know, this is due to the way Node's process.nextTick works. So the call to the callback happens after the test fails. Is there a way I can fix this test without relying on third-party libraries (e.g. q-flush)?
You're performing an asynchronous action so it's best to perform an asynchronous test. Adding a setTimeout still leaves you prone to race conditions.
describe('#insert', function () {
it('should delegate to db.insert', function () { // no done here
// note the return here to signal to mocha this is a promise test
return wrapper.insert({herp: 'derp'}).then(function(){
// add expects here, rest of asserts should happen here
expect(wrapper.db.insert.bind).to.have.been.calledOnce;
});
})
});
});
I'm just giving a try to unit testing in javascript/coffeescript with jasmine, very nice.
I've been trying to use jasmine.Clock.Mock() to advance in time and fire setTimeout callbacks.
Alas the jasmine.Clock.tick(1001) did not seem to have any effect !
I then discovered sinon.js that had its own time mock, and using this one it was allright. I'd like to understand why.
Here is a dummy jquery plugin to be tested:
dummy_method = function(callback) {
fire_callback = function() {
callback();
}
setTimeout("fire_callback()", 1000);
}
And here are both versions of the specs :
# Working test (spy was called as expected), using sinon FakeTimers
describe "jQuery.fn.countdown", ->
beforeEach () ->
this.clock = sinon.useFakeTimers();
afterEach () ->
this.clock.restore()
it 'should fireup the callback', ->
countdown_callback = jasmine.createSpy('countdown_callback');
dummy_method(countdown_callback)
this.clock.tick(1001)
expect(countdown_callback).toHaveBeenCalled()
# Non-working test (spy is never called), using jasmine Clock Mock
describe "jQuery.fn.countdown", ->
beforeEach () ->
jasmine.Clock.useMock()
it 'should fireup the callback', ->
countdown_callback = jasmine.createSpy('countdown_callback');
dummy_method(countdown_callback)
jasmine.Clock.tick(1001)
expect(countdown_callback).toHaveBeenCalled()
Jasmine just try to call a function where sinon test if the passed argument is a function or a string. If its a string it call eval.
Jasmine:
jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
if (jasmine.Clock.installed.setTimeout.apply) {
return jasmine.Clock.installed.setTimeout.apply(this, arguments);
} else {
return jasmine.Clock.installed.setTimeout(funcToCall, millis);
}
};
Sinon:
if (typeof timer.func == "function") {
timer.func.apply(null, timer.invokeArgs);
} else {
eval(timer.func);
}
So this will pass the Jasmin test
setTimeout(fire_callback, 1000);
while this will fail
setTimeout("fire_callback()", 1000);