mysterlune mysterlune - 6 months ago 11
Node.js Question

Unit testing Node.js and WebSockets (Socket.io)

Could anyone provide a rock-solid, dead-simple unit test for Node.js using WebSockets (Socket.io)?

I'm using socket.io for Node.js, and have looked at socket.io-client for establishing the client connection to a server in the test. However, I seem to be missing something.

In the example below, "worked..." never gets printed out.

var io = require('socket.io-client')
, assert = require('assert')
, expect = require('expect.js');

describe('Suite of unit tests', function() {

describe('First (hopefully useful) test', function() {

var socket = io.connect('http://localhost:3001');
socket.on('connect', function(done) {
console.log('worked...');
done();
});

it('Doing some things with indexOf()', function() {
expect([1, 2, 3].indexOf(5)).to.be.equal(-1);
expect([1, 2, 3].indexOf(0)).to.be.equal(-1);
});

});
});


Instead, I simply get:

Suite of unit tests
First (hopefully useful) test
✓ Doing some things with indexOf()


1 test complete (26 ms)


Any suggestions?

Answer

After further poking and prodding, I found some incredibly useful information at http://blog.foundry376.com/2012/09/connecting-to-a-socket-io-server-from-node-js-unit-tests. In the author's example, he points out the critical step of establishing socket listeners in the "before*" hooks. This example works (assuming a server is listening for socket connections at localhost:3001, of course)

var io = require('socket.io-client')
, assert = require('assert')
, expect = require('expect.js');

describe('Suite of unit tests', function() {

    var socket;

    beforeEach(function(done) {
        // Setup
        socket = io.connect('http://localhost:3001', {
            'reconnection delay' : 0
            , 'reopen delay' : 0
            , 'force new connection' : true
        });
        socket.on('connect', function() {
            console.log('worked...');
            done();
        });
        socket.on('disconnect', function() {
            console.log('disconnected...');
        })
    });

    afterEach(function(done) {
        // Cleanup
        if(socket.connected) {
            console.log('disconnecting...');
            socket.disconnect();
        } else {
            // There will not be a connection unless you have done() in beforeEach, socket.on('connect'...)
            console.log('no connection to break...');
        }
        done();
    });

    describe('First (hopefully useful) test', function() {

        it('Doing some things with indexOf()', function(done) {
            expect([1, 2, 3].indexOf(5)).to.be.equal(-1);
            expect([1, 2, 3].indexOf(0)).to.be.equal(-1);
            done();
        });

        it('Doing something else with indexOf()', function(done) {
            expect([1, 2, 3].indexOf(5)).to.be.equal(-1);
            expect([1, 2, 3].indexOf(0)).to.be.equal(-1);
            done();
        });

    });

});

I found that the placement of done() in the beforeEach, socket.on('connect'...) listener was crucial to having the connection get established. For example, if you comment out done() in the listener, then add it one scope out (just before exiting the beforeEach), you'll see the "no connection to break..." message instead of the "disconnecting..." message. Like so:

beforeEach(function(done) {
    // Setup
    socket = io.connect('http://localhost:3001', {
        'reconnection delay' : 0
        , 'reopen delay' : 0
        , 'force new connection' : true
    });
    socket.on('connect', function() {
        console.log('worked...');
        //done();
    });
    socket.on('disconnect', function() {
        console.log('disconnected...');
    });
    done();
});

I'm new to Mocha, so there's probably a very obvious reason to the initiated for placing done() withiin the socket scope itself. Hopefully that little detail will save others in my shoes from hair pulling.

For me, the above test (with correct scoping of done()) outputs:

  Suite of unit tests
    First (hopefully useful) test
      ◦ Doing some things with indexOf(): worked...
      ✓ Doing some things with indexOf() 
disconnecting...
disconnected...
      ◦ Doing something else with indexOf(): worked...
      ✓ Doing something else with indexOf() 
disconnecting...
disconnected...


  2 tests complete (93 ms)
Comments