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;
});
})
});
});
Related
I have a function, which computes some stuff, notifying the user via callbacks about some events:
function returnAndCallback(callback) {
callback(5); // not always invoked
return 3;
}
Using Mocha and Should.js, I wrote this test:
describe('mixing sync and async code', function() {
it('should test callback AND return value', function(done) {
returnAndCallback(function(value) {
value.should.be.eql(5);
done();
}).should.be.eql(4);
});
});
This succeeds because the test ends when done() is called. It seems, I can either write a synchronous test and check the return value, or I can write an asynchronous test and check the callback.
One can not use a sync test like this:
describe('mixing sync and async code', function() {
it('should test callback AND return value', function() {
returnAndCallback(function(value) {
should.be.eql('difficult to get result');
}).should.be.eql(3);
});
});
... because I want to assert that the callback is called. This test would succeed, if the callback is never called.
How can I test both that the callback is called with the right value AND the correct value is returned?
Duplicating tests is the only option I see.
Edit: I notice, that I use the term asynchron wrongly here. The callbacks are merely a way to inject optional actions into a function, which transforms an object. All code is synchronous, but the control flow sometimes branches of into callbacks and I want to be able to recognize that.
Here is another possible way to test that:
describe('mixing sync and async code', function() {
it('should test callback AND return value', function() {
let callbackValue;
returnAndCallback(function(value) {
callbackValue = value;
}).should.be.eql(3);
callbackValue.should.be.eql(5);
});
});
But still not perfectly elegant due to the extra variable.
First, be aware that done() implies a synchronous test; Mocha's default is to run tests asynchronously. If you want to test the 'returned' value from asynchronous functions (functions that return a value in a callback function), you run them synchronously, via done().
Next, you can't return a value from an asynchronous function. These two behaviours are mutually exclusive:
function returnAndCallback(callback) {
callback(5); // not always invoked
return 3;
}
You want to only execute the callback.
It appears to me that you're expecting that sometimes a callback is passed, but not always. In that case, I'd separate the function tests (I think you'll need to use done() everywhere, to persist the synchronous nature of the tests) and do a check for callback inside the function itself.
Now that we've got that clarified, since you want to assert that a callback is called, we need to establish some baseline assumptions:
A) A callback should be a function
B) A callback should be called with a parameter that contains a value
You want to test for both of these things. A) is easy to prove: you're writing the callback function as part of your test, so if you passed say, null or undefined, of course the test will fail, but that's not the point of this test. Here is how you prove both A) and B):
function optionalAsync(callback) {
if (typeof callback === 'function') {
callback(4)
} else {
return 3
}
}
describe('optional async function', function() {
it('should return 4 when a callback is passed', function(done) {
optionalAsync(function(value) {
should(value).be.eql(4)
done()
})
})
it('should return 3 when no callback is passed', function(done) {
optionalAsync().should.be.eql(3)
done()
})
})
This is kind of strange, but given your use case, it does make sense to check for both possibilities. I'm sure you could reduce the code footprint a bit too, but I'd suggest keeping it this way for the sake of readability for when you shelve tis for a year and forget what you did ;)
Now after all of this if you still want to have the option for a function to run synchronously you can do so by blocking the event loop: https://stackoverflow.com/a/22334347/1214800.
But why would you want to?
Save yourself the trouble of handling synchronous operations in an inherently non-blocking, asynchronous platform, and write everything (even the non-IO-blocking operations) with a callback:
function optionallyLongRunningOp(obj, callback) {
if (typeof callback === 'function') {
validateObject(obj, function(err, result) {
// not always long-running; may invoke the long-running branch of your control-flow
callback(err, result)
})
} else {
throw new Error("You didn't pass a callback function!")
}
}
describe('possibly long-running op async function', function() {
it('should return 4 when control flow A is entered', function(done) {
obj.useControlFlow = "A"
validateObject(obj, function(err, result) {
// this is a slow return
should(result.value).be.eql(4)
done()
})
it('should return 3 when control flow B is entered', function(done) {
obj.useControlFlow = "B"
validateObject(obj, function(err, result) {
// this is a quick return
should(result.value).be.eql(3)
done()
})
})
})
Here is your answer written with everything as callback (even the short ops):
var doLongRunnignOp = function(cb) {
var didNotify = true
cb(didNotify)
}
function doubleAndNotifyEven(num, cb) {
if (num % 2 == 0) {
doLongRunnignOp(function(didNotify) {
cb(num)
// did notify
})
} else {
cb(2 * num)
// instant return, did not notify
}
}
describe('checking return value and callback execution', function() {
it('should double and report given an even number', function() {
doubleAndNotifyEven(2, function(value) {
should(value).be.eql(2)
})
})
it('should double and not report anything given an odd number', function() {
doubleAndNotifyEven(3, function(value) {
should(value).be.eql(6)
})
})
})
Here is another solution for this problem. It just adds an additional line of code in tests which want to ensure callback execution.
let should = require('should');
function doubleAndNotifyEven(num, reportEven) {
if (num % 2 == 0) {
reportEven(num);
}
return 2 * num;
}
function mandatoryCallback(callback) {
mandatoryCallback.numCalled = 0;
return function () {
mandatoryCallback.numCalled++;
return callback.apply(this, arguments);
};
}
describe('checking return value and callback execution', function() {
it('should double and report given an even number', function() {
doubleAndNotifyEven(2, mandatoryCallback(function(value) {
should(value).be.eql(2);
})).should.be.eql(4);
should(mandatoryCallback.numCalled).greaterThan(0);
});
it('should double and not report anything given an odd number', function() {
doubleAndNotifyEven(3, function(value) {
throw new Error('Wrong report!');
}).should.be.eql(6);
});
});
Please also note sinon which does something similar.
Hello I have problem with .then() function while trying to implement jasmine unit testing.
Here is my code:
describe("getBuilding", function () {
it("checks getBuilding", function () {
var id_building = 4;
LocalDB.getTestData();
LocalDB.getBuilding(id_building).then(function (result) {
expect(result.name).toMatch("Something");
});
});
});
In this case, the result variable has a right value in then() function, but expect just doesnt work here. If i change "Something" to "something else" the tests will still succes, althought it should't.
I tried to solve it like this:
describe("getBuilding", function () {
it("checks getBuilding", function () {
var id_building = 4;
LocalDB.getTestData();
expect(LocalDB.getBuilding(id_building).name).toMatch("Something");
});
});
or
describe("getBuilding", function () {
it("checks getBuilding", function () {
var finalResult;
var id_building = 4;
LocalDB.getTestData();
LocalDB.getBuilding(id_building).then(function (result) {
finalResult=result.name;
});
expect(finalResult).toMatch("Something");
});
});
But in both cases, the value that's being matched is undefined. Can anyone give some advice pls?
Your 'then()' is probably not being run at all - since the promise is resolved asynchronously, you need to either ensure that the promises are resolved before exiting the test or use jasmine async to ensure that jasmine waits for the async method to resolve before moving on to the next test.
In unit tests with promises, often you need to manually notify the angularjs lifecycle that it's time for promises to be resolved.
Try bringing in the $rootScope dependency and adding a call to $rootScope.$digest() at the end of your test.
describe("getBuilding", function () {
it("checks getBuilding", function () {
var id_building = 4;
LocalDB.getTestData();
LocalDB.getBuilding(id_building).then(function (result) {
expect(result.name).toMatch("Something");
});
$rootScope.$digest();
});
});
If that doesn't work by itself, you may also need to use Jasmine Async
describe("getBuilding", function () {
it("checks getBuilding", function (done) {
var id_building = 4;
LocalDB.getTestData();
LocalDB.getBuilding(id_building).then(function (result) {
expect(result.name).toMatch("Something");
done();
}, function(){
done.fail('The promise was rejected');
});
$rootScope.$digest();
});
});
The jasmine async way using done is a better test, because it will fail if the promise is rejected. The first would silently pass if the promise was rejected. However, if you know that this particular promise will always resolve, the first way may be good enough for your scenario.
According to :http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support
Jasmine has support for async methods. Essentially your issue is your asserting before your async call has responded. Therefore it has no data and your assert fails.
So according to the above link you could do something like (not tested, i haven't used jasmine so cannot be certain im following the link correctly either. Hopefully you can understand better than i).
describe("getBuilding", function () {
it("checks getBuilding", function () {
var id_building = 4;
LocalDB.getBuilding(id_building).then(function (result) {
expect(result.name).toMatch("Something");
done();
});
});
});
Try to return the promise.
return LocalDB.getBuilding(id_building)
.then(function (result) {
expect(result.name).toMatch("Something");
});
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'm trying to create a module that will fill in form inputs when functional testing, and I'd like to be able to call it from multiple test suites.
Pseudo code for the helper file (helper.js)
module.exports = {
fillForm: function() {
this.findByCssSelector('#firstname')
.click()
.pressKeys('John')
.end()
},
anotherFunction: function() {
// more code
}
}
In the spec for the functional test, I load that module as helper and I can see it execute. However, it seems I can't use this syntax and guarantee that the chained steps execute in the defined order:
'Test filling form data': function() {
return this.remote
.get(require(toUrl(url))
// should happen first
.then(helper.fillForm)
// should happen second
.then(helper.anotherFunction)
// only after the above should the click happen
.findByCsSelector('#submit')
// click evt should show the #someElement element
.click()
.findByCssSelector('#someElement')
.getComputedStyle('display')
.then(style) {
// assertions here
}
It seems that the promise chaining allows the click event to happen before the then callbacks have executed. Is this sort of flow possible with intern?
UPDATE:
For the moment, working around this with this sort of code:
var remote = initTest.call(this, url);
return helpers.fillForm1Data.call(remote)
.otherChainedMethodsHere()
.moreChainedMethods()
.then() {
// assertion code here
where the initTest method does url fetching, window sizing, clearing data, and the fillForm1Data does as you'd expect. But the syntax is pretty ugly this way.
Your helper is not returning any value so it is treated as a synchronous callback and the next thing in the chain is executed immediately. You also cannot return this from a promise helper or it will cause a deadlock (because the Command promise will be waiting for itself to resolve—Intern will throw an error instead if you try to do this), so you need to create a new Command and return that if you want to use the chained Command interface within your helper:
module.exports = {
fillForm: function() {
return new this.constructor(this.session)
.findByCssSelector('#firstname')
.click()
.pressKeys('John');
},
anotherFunction: function() {
// more code
}
};
You can also just return from this.session instead if you don’t care about the convenience of the Command API and can deal with normal promise callback chains:
module.exports = {
fillForm: function() {
var session = this.session;
return session.findByCssSelector('#firstname')
.then(function (element) {
return element.click();
})
.then(function () {
return session.pressKeys('John');
});
},
anotherFunction: function() {
// more code
}
};