mocha should expect a timeout, call done() when timed out - node.js

I'm trying to write a test for socket. I'm using passport.socketio and so socket shouldn't be connected (and thus the socket callback never fired) when there's no logged in user. I want to test that.
Is there a way to actually expect a timeout?
describe('Socket without logged in user', function() {
it('passport.socketio should never let it connect', function(done) {
socket.on('connect', function() {
// this should never happen, is the expected behavior.
});
});
});
Or any other way I should approach this?

You can basically program it yourself:
var EXPECTED_TIMEOUT = 2000; // This value should be lesser than the actual mocha test timeout.
it('passport.socketio should never let it connect', function(done) {
this.timeout(EXPECTED_TIMEOUT + 100); // You add this to make sure mocha test timeout will only happen as a fail-over, when either of the functions haven't called done callback.
var timeout = setTimeout(done, EXPECTED_TIMEOUT); // This will call done when timeout is reached.
socket.on('connect', function() {
clearTimeout(timeout);
// this should never happen, is the expected behavior.
done(new Error('Unexpected call'));
});
});
You can also use addTimeout module to shorten the code:
var EXPECTED_TIMEOUT = 2000; // This value should be lesser than the actual mocha test timeout.
it('passport.socketio should never let it connect', function(done) {
this.timeout(EXPECTED_TIMEOUT + 100); // You add this to make sure mocha test timeout will only happen as a fail-over, when either of the functions haven't called done callback.
function connectCallback() {
done(new Error('Unexpected Call'));
}
socket.on('connect', addTimeout(EXPECTED_TIMEOUT, connectCallback, function () {
done()
});
});

Related

Testing with Mocha in Node.js

I'm getting really inconsistent behavior in my terminal when console.logging inside of a test I wrote using mocha. We are running a node.js server and running socket.io. Does the console.log not go to the terminal only some of the time for some reason? I'm really confused about this behavior.
➜ tests git:(master) ✗ mocha test-chat-server.js
hello world
echo
✓ echos message (68ms)
On Connect Things Should Happen
✓ initial connection events
disconnected
i'm here
2 passing (93ms)
➜ tests git:(master) ✗ mocha test-chat-server.js
hello world
echo
✓ echos message (61ms)
On Connect Things Should Happen
✓ initial connection events
2 passing (77ms)
The difference between these two times I ran the mocha test are the console.log statements that appears in the first test run (disconnected, i'm here). They do not appear in the second test that I ran.
Edit: posting my test code in response to the comment (thank you!)
var should = require('should');
var socket = require('socket.io-client')('http://localhost:3000');
describe("echo", function () {
var server,
options ={
transports: ['websocket'],
'force new connection': true
};
it("echos message", function (done) {
var client = socket.connect("http://localhost:3000", options);
client.once("connect", function () {
client.once("echo", function (message) {
message.should.equal("Hello World");
client.disconnect();
done();
});
client.emit("echo", "Hello World");
});
done();
});
});
describe("On Connect Things Should Happen", function() {
it('initial connection events', function() {
should.exist(socket);
socket.open();
socket.compress(false).emit('an event', { some: 'data' });
socket.on('error', function() {
console.log('error');
});
socket.connect();
socket.on('disconnect', function(connection) {
console.log('disconnected');
console.log("i'm here");
});
socket.on('connect', function(connection) {
console.log('connected');
});
});
});
You are falling into the classic node async trap. Your "things should happen" test sometimes returns before the disconnect event happens and sometimes not.
You need to handle the done function the same way you do in the "echoes message" test. Punctually, it should like this:
socket.on('disconnect', function(connection) {
console.log('disconnected');
console.log("i'm here");
done()});
In general, I'm not sure how much that test makes handling all those different callbacks.

done method "ignored" in beforeEach in mochajs test

I have a unit test for my wrapper around a web socket client. Here is the code to the test:
describe('server', function(){
var server;
beforeEach(function(done) {
server = new Server(function() {
//try to connect to the server on the expected port
var socket = new WebSocket('ws://localhost:8081');
});
server.wss.on('connection', function(client) {
server.wss.close();
done();
});
});
describe('#server', function(){
it('starts a server on a given port', function(done) {
var test = 1;
test.should.be.ok;
});
});
});
the issue that i'm running into is that while done is called properly (if i call done a second time right after the first time, i get an error that it was called twice) it does not seem to have any effect. Namely the test will fail after two second with:
Error: timeout of 2000ms exceeded
I'm kind of new at this, so i probably missed something easy...
Thanks, olivier
As usual, once you post the question you find the answer.
The trick is to call done inside each of the tests too.
describe('server', function(){
var server;
beforeEach(function(done) {
server = new Server(function() {
//try to connect to the server on the expected port
var socket = new WebSocket('ws://localhost:8081');
});
server.wss.on('connection', function(client) {
server.wss.close();
done();
});
});
describe('#server', function(){
it('starts a server on a given port', function(done) {
var test = 1;
test.should.be.ok;
=====> done();
});
});
});

mongoskin+mocha: How to do clean-up in after() when assertion failed?

I do the clean-up in an after() call before any other describe. If all tests pass, the clean-up will do the job. But if any test fails, the clean-up code will receive an err: [Error: no open connections].
I think the assertion in the callback of mongodb throws an error cause the connection closed.
That make me confusing:
First, I think the callback of mongodb is the right place to put some assertions;
Second, the assertions will throw error when failed, and cause connection closes;
Finally, the clean-up will failed due to connection closed.
So, what else should I do to make clean-up to do its job even the assertion failed?
I have made a sample code below:
var mongo = require('mongoskin')
, should = require('should')
;
describe('mongo', function() {
var db;
before(function() {
console.log('before');
db = mongo.db('devstack.local:27017/test')
});
after(function(done) {
console.log('after');
db.dropDatabase(function(err) {
should.not.exist(err);// [Error: no open connections]
db.close(done);
});
});
describe('close', function() {
it('should count!=0', function(done) {
db.collection('empty').count(function(err, count) {
count.should.not.equal(0); // use an empty collection to make sure this fail
done();
});
})
})
})
Here's an hypothesis: the connection never happens.
When I run your test suite with:
db = mongo.db('nonexistent:3333/test')
instead of the address you have, I can completely reproduce your error. Note that:
count.should.not.equal(0); fails because count is undefined, not because any of the framework defined by the should module is called.
If I transform the test so that it checks err :
it('should count!=0', function(done) {
db.collection('empty').count(function(err, count) {
should.not.exist(err); // <<< This is where it fails now!
count.should.not.equal(0); // use an empty collection to make sure this fail
done();
});
});
Then the test fails at should.not.exist(err) and err is:
[Error: failed to connect to [nonexistent:3333]]
A couple of thoughts:
Always check err in your callbacks.
In the before callback which establishes the database connection, perform at least one operation which is guaranteed to fail if the connection is not established. You'd want an operation which is as inexpensive to perform as possible. I don't know Mongo very well but this seems to do the trick:
before(function (done) {
db = mongo.db(<put address here>, {safe: true});
db.open(function (err) {
should.not.exist(err);
done();
});
});
This way Mocha will detect the failure right away.

Client can only emit once and "force new connection" duplicates the response.

my client side can only emit once and "force new connection" duplicates the response back to my client. here's my code so you can look it up.
server.js
var app = require('http').createServer()
, io = require('socket.io').listen(app);
app.listen(5000);
io.sockets.on('connection', function(socket) {
socket.on('sendSheet', function(data) {
io.sockets.emit('displayData', data);
});
socket.on('disconnect', function() {
io.sockets.emit('user disconnected');
});
});
client.js
var socket = io.connect('http://localhost:5000', {'force new connection': true});
socket.on('dispatchConnect', function (data) {
socket.emit('sendSheet', mergedForm);
});
socket.on('displayData', function (data) {
console.log(data);
});
Read about asynchronous functions in nodejs and try to understand "the node event loop".
Your code is blocking couse your functions are synchronous .
Since an event loop runs in a single thread, it only processes the next event when the callback finishes.
You should never use a blocking function inside a callback, since you’re blocking the event loop
and preventing other callbacks - probably belonging to other client connections - from being served.
Here is a async example:
var myAsyncFunction = function(someArg, callback) { // simulate some I/O was done
setTimeout(function() { // 1 second later, we are done with the I/O, call the callback
callback();
}, 1000)
}

In mocha testing while calling asynchronous function how to avoid the timeout Error: timeout of 2000ms exceeded

In my node application I'm using mocha to test my code. While calling many asynchronous functions using mocha, I'm getting timeout error (Error: timeout of 2000ms exceeded.). How can I resolve this?
var module = require('../lib/myModule');
var should = require('chai').should();
describe('Testing Module', function() {
it('Save Data', function(done) {
this.timeout(15000);
var data = {
a: 'aa',
b: 'bb'
};
module.save(data, function(err, res) {
should.not.exist(err);
done();
});
});
it('Get Data By Id', function(done) {
var id = "28ca9";
module.get(id, function(err, res) {
console.log(res);
should.not.exist(err);
done();
});
});
});
You can either set the timeout when running your test:
mocha --timeout 15000
Or you can set the timeout for each suite or each test programmatically:
describe('...', function(){
this.timeout(15000);
it('...', function(done){
this.timeout(15000);
setTimeout(done, 15000);
});
});
For more info see the docs.
I find that the "solution" of just increasing the timeouts obscures what's really going on here, which is either
Your code and/or network calls are way too slow (should be sub 100 ms for a good user experience)
The assertions (tests) are failing and something is swallowing the errors before Mocha is able to act on them.
You usually encounter #2 when Mocha doesn't receive assertion errors from a callback. This is caused by some other code swallowing the exception further up the stack. The right way of dealing with this is to fix the code and not swallow the error.
When external code swallows your errors
In case it's a library function that you are unable to modify, you need to catch the assertion error and pass it onto Mocha yourself. You do this by wrapping your assertion callback in a try/catch block and pass any exceptions to the done handler.
it('should not fail', function (done) { // Pass reference here!
i_swallow_errors(function (err, result) {
try { // boilerplate to be able to get the assert failures
assert.ok(true);
assert.equal(result, 'bar');
done();
} catch (error) {
done(error);
}
});
});
This boilerplate can of course be extracted into some utility function to make the test a little more pleasing to the eye:
it('should not fail', function (done) { // Pass reference here!
i_swallow_errors(handleError(done, function (err, result) {
assert.equal(result, 'bar');
}));
});
// reusable boilerplate to be able to get the assert failures
function handleError(done, fn) {
try {
fn();
done();
} catch (error) {
done(error);
}
}
Speeding up network tests
Other than that I suggest you pick up the advice on starting to use test stubs for network calls to make tests pass without having to rely on a functioning network. Using Mocha, Chai and Sinon the tests might look something like this
describe('api tests normally involving network calls', function() {
beforeEach: function () {
this.xhr = sinon.useFakeXMLHttpRequest();
var requests = this.requests = [];
this.xhr.onCreate = function (xhr) {
requests.push(xhr);
};
},
afterEach: function () {
this.xhr.restore();
}
it("should fetch comments from server", function () {
var callback = sinon.spy();
myLib.getCommentsFor("/some/article", callback);
assertEquals(1, this.requests.length);
this.requests[0].respond(200, { "Content-Type": "application/json" },
'[{ "id": 12, "comment": "Hey there" }]');
expect(callback.calledWith([{ id: 12, comment: "Hey there" }])).to.be.true;
});
});
See Sinon's nise docs for more info.
If you are using arrow functions:
it('should do something', async () => {
// do your testing
}).timeout(15000)
A little late but someone can use this in future...You can increase your test timeout by updating scripts in your package.json with the following:
"scripts": {
"test": "test --timeout 10000" //Adjust to a value you need
}
Run your tests using the command test
For me the problem was actually the describe function,
which when provided an arrow function, causes mocha to miss the
timeout, and behave not consistently. (Using ES6)
since no promise was rejected I was getting this error all the time for different tests that were failing inside the describe block
so this how it looks when not working properly:
describe('test', () => {
assert(...)
})
and this works using the anonymous function
describe('test', function() {
assert(...)
})
Hope it helps someone, my configuration for the above:
(nodejs: 8.4.0, npm: 5.3.0, mocha: 3.3.0)
My issue was not sending the response back, so it was hanging. If you are using express make sure that res.send(data), res.json(data) or whatever the api method you wanna use is executed for the route you are testing.
Make sure to resolve/reject the promises used in the test cases, be it spies or stubs make sure they resolve/reject.

Resources