I'm writing test cases with mocha and its hook beforeEach which deletes and re-creates all tables using sequelize.drop and sequelize.sync.
lib/testutils.js
exports.deleteAll = function () {
return sequelize.drop({logging: false, cascade: true}).then(function() {
return sequelize.sync({logging: false});
});
};
test/controllers/controllers.js
(A) This did work:
var testutils = require("../../lib/testutils");
describe("CRUD Controller", function() {
beforeEach(function(done) {
testutils.deleteAll().then(function(){
done();
}).catch(function(err) {
return done(err);
});
});
describe("#read()", function(){
it("should....", function(done) {
});
});
}
(B) This did not work:
var testutils = require("../../lib/testutils");
describe("CRUD Controller", function() {
beforeEach(function(done) {
testutils.deleteAll().then(done).catch(function(err) {
return done(err);
});
});
describe("#read()", function(){
it("should....", function(done) {
});
});
}
I don't understand why testutils.deleteAll().then(done) did not work and the first test case it("should....", function(done) did not wait for the beforeEach hook finished. I was getting TypeError: Converting circular structure to JSON. However, testutils.deleteAll().then(function(){ done(); }) did work.
My question is why (B) is not working while (A) is working? Any idea? Is there something wrong?
Mocha supports promises in beforeEach so just write below code:
beforeEach(function(){
return testutils.deleteAll();
});
Your deleteAll() function returns sequelize.sync() which returns a promise passing it this. I think the context will be the last model created, so when you pass a function to the returned promise, the function will be passed the last table created. So in case (A) you specified the function()
and called done() manually without passing any args to done(). But in case (B) when you pass done to the promise.then(), the last model created will be passed to done.
To get what I mean consider the following:
User.create({name: 'aName'}).then(function(user){console.log(user)});
is equivalent to
User.create({name: 'aName'}).then(console.log);
The user will be automatically passed to console.log().
So done was being passed the last model created which I think might cause a problem.
When you pass done directly it receive object returned from deleteAll, and mocha treat it as an error, so it try apply JSON.stringify to this object and show it as an error, but somehow JSON.stringify can't do it, so it throw exception which you can see.
You can clearly see it in Mocha::Runnable#run
Related
I have a restify action code block below:
function retriveAll(req, res, next) {
db.user
.find({where: {id: 1})
.then(function(user){
res.send(user);
})
.catch(function(details){
res.send(details.message);
})
.finally(function(){
next();
});
}
I want to test this action specifically validating that res.send() was called within this code block. And later on validating the res.send() returned data. I'm using SinonJs and Mocha for testing framework. Here's a sample test code block for the method above.
describe('retrieveAll()', function() {
reqStub = {};
resStub = {send: sinon.stub()};
nextStub = sinon.stub();
beforeEach(function() {
module.retrieveAll(reqStub, resStub, nextStub);
});
// this doesn't work
// and the sub.calledCount is 0
// i wonder if it's because the res.send() is inside a Promise code block???
// if I move the res.send() out from Promise, just before next(), then it works
it('should call res.send()', function() {
sinon.assert.calledOnce(resStub.send);
});
// this one works
it('should call next', function() {
sinon.assert.calledOnce(nextStub);
});
});
Could someone shed some light?
The beforeEach()'s callback function receives a done parameter that can be called to signal an asynchronous completion. Since your retriveAll function calls the last parameter (next) as the last action, you can pass that parameter as the next value and it should work:
beforeEach(function(done) {
module.retrieveAll(reqStub, resStub, done);
});
You will however loose the nextStub, so... alternatively, you could spy on that done function:
describe('retrieveAll()', function() {
var reqStub = {};
var resStub = {send: sinon.stub()};
var nextSpy;
beforeEach(function(done) {
nextSpy = sinon.spy(done);
module.retrieveAll(reqStub, resStub, done);
});
// this doesn't work
// and the sub.calledCount is 0
// i wonder if it's because the res.send() is inside a Promise code block???
// if I move the res.send() out from Promise, just before next(), then it works
it('should call res.send()', function() {
sinon.assert.calledOnce(resStub.send);
});
// this one works
it('should call next', function() {
sinon.assert.calledOnce(nextSpy);
});
});
So, I got this working thanks for #Amit for pointing me to the done callback on beforeEach
First, I modified the retrieveAll so the next callback is included in the promise chain. I put it in the finally handler making sure that next will be called after all process.
Second, I passed the done to beforeEach and then nextStub will spy on done callback.
Third, instead of passing done cb to module.v1.retrieveAll I used nextStub. This solve the issue on testing nextStub.calledOnce.
The updated code now looks:
function retriveAll(req, res, next) {
db.user
.find({where: {id: 1})
.then(function(user){
res.send(user);
})
.catch(function(details){
res.send(details.message);
})
.finally(function(){
next();
});
}
describe('retrieveAll()', function() {
var reqStub = {};
var resStub = {send: sinon.stub()};
var nextStub;
beforeEach(function(done) {
nextStub = sinon.spy(done);
module.retrieveAll(reqStub, resStub, nextStub);
});
// this doesn't work
// and the sub.calledCount is 0
// i wonder if it's because the res.send() is inside a Promise code block???
// if I move the res.send() out from Promise, just before next(), then it works
it('should call res.send()', function() {
sinon.assert.calledOnce(resStub.send);
});
// this one works
it('should call next', function() {
sinon.assert.calledOnce(nextStub);
});
});
I going to choose #Amit answer as best answer since he helped and gave me clues on the changes.
I'm trying to test my REST API endpoint handlers using Mocha and Chai, the application was built using Express and Mongoose. My handlers are mostly of the form:
var handler = function (req, res, next) {
// Process the request, prepare the variables
// Call a Mongoose function
Model.operation({'search': 'items'}, function(err, results) {
// Process the results, send call next(err) if necessary
// Return the object or objects
return res.send(results)
}
}
For example:
auth.getUser = function (req, res, next) {
// Find the requested user
User.findById(req.params.id, function (err, user) {
// If there is an error, cascade down
if (err) {
return next(err);
}
// If the user was not found, return 404
else if (!user) {
return res.status(404).send('The user could not be found');
}
// If the user was found
else {
// Remove the password
user = user.toObject();
delete user.password;
// If the user is not the authenticated user, remove the email
if (!(req.isAuthenticated() && (req.user.username === user.username))) {
delete user.email;
}
// Return the user
return res.send(user);
}
});
};
The problem with this is that the function returns as it calls the Mongoose method and test cases like this:
it('Should create a user', function () {
auth.createUser(request, response);
var data = JSON.parse(response._getData());
data.username.should.equal('some_user');
});
never pass as the function is returning before doing anything. Mongoose is mocked using Mockgoose and the request and response objects are mocked with Express-Mocks-HTTP.
While using superagent and other request libraries is fairly common, I would prefer to test the functions in isolation, instead of testing the whole framework.
Is there a way to make the test wait before evaluating the should statements without changing the code I'm testing to return promises?
You should use an asynchronous version of the test, by providing a function with a done argument to it.
For more details refer to http://mochajs.org/#asynchronous-code.
Since you don't want to modify your code, one way to do that could be by using setTimeout in the test to wait before to call done.
I would try something like this:
it('Should create a user', function (done) {
auth.createUser(request, response);
setTimeout(function(){
var data = JSON.parse(response._getData());
data.username.should.equal('some_user');
done();
}, 1000); // waiting one second to perform the test
});
(There might be better way)
Apparently, express-mocks-http was abandoned a while ago and the new code is under node-mocks-http. Using this new library it is possible to do what I was asking for using events. It's not documented but looking at the code you can figure it out.
When creating the response object you have to pass the EventEmitter object:
var EventEmitter = require('events').EventEmitter;
var response = NodeMocks.createResponse({eventEmitter: EventEmitter});
Then, on the test, you add a listener to the event 'end' or 'send' as both of them are triggered when the call to res.send. 'end' covers more than 'send', in case you have calls other than res.send (for example, res.status(404).end().
The test would look something like this:
it('Should return the user after creation', function (done) {
auth.createUser(request, response);
response.on('send', function () {
var data = response._getData();
data.username.should.equal('someone');
data.email.should.equal('asdf2#asdf.com');
done();
});
});
Running this with mocha results in timing out, rather than letting mocha catch the error so it could fail immediately..
var when = require('when');
var should = require('should');
describe('', function() {
it('', function(done) {
var d = when.defer();
d.resolve();
d.promise.then(function() {
true.should.be.false;
false.should.be.true;
throw new Error('Promise');
done();
}); }); });
http://runnable.com/me/U7VmuQurokZCvomD
Is there another way to make assertions inside the promise, such that when they fail they are caught by mocha causing it to fail immediately?
As per chai recommendation, I looked into it and it seems I have to have a direct access to the promise object, right? The problem is that I'm not using promise directly.. My bad if I simplified but This would be a more closer to reality example
function core_library_function(callback){
do_something_async(function which_returns_a(promise){
promise.then(function(){
callback(thing);
}); }); }
describe('', function() {
it('', function(done) {
core_library_function(function(thing){
...
done();
}); }); });
So I really have no control over the promise directly, it's abstracted far far away.
When using promises with Mocha, you'll have to return the promise in the test and will want to remove the done parameter since the callback isn't being used.
it('', function() {
var d = when.defer();
d.resolve();
return d.promise.then(function() {
throw new Error('Promise');
});
});
This is described in the docs under Working with Promises:
Alternately, instead of using the done() callback, you can return a promise.
Why does mocha timeout when an assertion fails inside a Q future? You also don't get to see the assertion failure when this happens. This does not happen if I just use callbacks. How should I write this while still using futures but get to see the assertion error instead of a timeout?
var Q = require('q');
function hack() {
var ret = Q.defer();
ret.resolve(true);
return ret.promise;
}
it('test', function(done) {
hack().then(function(bool) {
assert(false);
done();
});
});
The assertion call throws an exception, which is caught by Q in order to properly conform to the promises spec. So mocha never reaches done(), nor does it see an exception thrown. You could do something like this:
it('test', function(done) {
hack().then(function(bool) {
assert(false);
done();
}).catch(function(err){
done(err);
});
});
[edit] Alternatively, you can omit the done argument altogether and just return the promise directly from the test function, in which case mocha will pass/fail the test based on the outcome of the returned promise:
it('test', function() {
return hack().then(function(bool) {
assert(false);
});
});
...which is a nice way to simplify your test functions. Props to Taytay elsewhere in this thread for pointing this out.
Mocha now supports promises in unit tests, so you can just return the promise instead of relying upon calling (done) from a then handler. It's easier and safer (because you won't forget to call done)
So you could just write:
it('test', function() {
return hack().then(function(bool) {
assert(false);
});
});
That would fail because the promise would fail, and Mocha would detect it.
This is from the Mocha docs in the section "Working with Promises": https://mochajs.org/
Improving on greim's answer including what callumacrae added in a comment, you can do it like this:
it('test', function(done) {
hack().then(function(bool) {
assert(false);
done();
}).catch(done);
});
When I'm testing with Mocha, I often have a combination of both asynchronous and synchronous tests that need to run.
Mocha handles this beautifully allowing me to specify a callback, done, whenever my tests are asynchronous.
My question is, how does Mocha internally observe my tests and know that it should wait for asynchronous activity? It seems to wait anytime I have the callback parameter defined in my test functions. You can see in the examples below, the first test should timeout, the second should proceed and finish before user.save calls the anonymous function.
// In an async test that doesn't call done, mocha will timeout.
describe('User', function(){
describe('#save()', function(){
it('should save without error', function(done){
var user = new User('Luna');
user.save(function(err){
if (err) throw err;
});
})
})
})
// The same test without done will proceed without timing out.
describe('User', function(){
describe('#save()', function(){
it('should save without error', function(){
var user = new User('Luna');
user.save(function(err){
if (err) throw err;
});
})
})
})
Is this node.js specific magic? Is this something that can be done in any Javascript?
This is simple pure Javascript magic.
Functions are in fact objects, and they have properties (such as the number of parameters are defined with the function).
Look at how this.async is set in mocha/lib/runnable.js
function Runnable(title, fn) {
this.title = title;
this.fn = fn;
this.async = fn && fn.length;
this.sync = ! this.async;
this._timeout = 2000;
this._slow = 75;
this.timedOut = false;
}
Mocha's logic changes based whether or not your function is defined with parameters.
What you're looking for is Function's length property which can tell how many arguments a function is expecting. When you define a callback with done it can tell and treats it asynchonously.
function it(str, cb){
if(cb.length > 0)
//async
else
//sync
}
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/Length