Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test node.js websocket server?

I'm using sockjs with standard configuration.

   var ws = sockjs.createServer();
    ws.on('connection', function(conn) {
        conn.on('data', function(message) {
            wsParser.parse(conn, message)
        });
        conn.on('close', function() {

        });
    });

    var server = http.createServer(app);
    ws.installHandlers(server, {prefix:'/ws'});
    server.listen(config.server.port, config.server.host);

wsParser.parse function works like this:

function(conn, message) {

(...)

switch(message.action) {
    case "titleAutocomplete":
        titleAutocomplete(conn, message.data);
        break;
    (...) // a lot more of these
 }

Each method called in switch sends back a message to client.

var titleAutocomplete = function(conn, data) {

    redis.hgetall("titles:"+data.query, function(err, titles){
        if(err) ERR(err);

        if(titles) {
            var response = JSON.stringify({"action": "titleAutocomplete", "data": {"titles": titles}});
            conn.write(response);
        } 
    })
};

Now my problem is that I'd like to make tests for my code (better late than never I guess) and I have no idea how to do it. I started writing normal http tests in with mocha + supertest but I just don't know how to handle websockets.

I'd like to have only one websocket connection to reuse through all tests, I'm binding the websocket connection with user session after first message and I want to test that persistence as well.

How do I make use of ws client's onmessage event and utilize it in my tests? How the tests can tell apart received messages and know which one they are supposed to wait for?

like image 662
kasztelan Avatar asked Jan 22 '14 20:01

kasztelan


1 Answers

Collegue at work asked if it really needs to be a client connection or would it be possible to just mock it up. It turned out it was the way to go. I wrote a little helper class wsMockjs

var wsParser = require("../wsParser.js");

exports.createConnectionMock = function(id) {
    return {
        id: id,
        cb: null,
        write: function(message) {
            this.cb(message);
        },
        send: function(action, data, cb) {
            this.cb = cb;
            var obj = {
                action: action,
                data: data
            }
            var message = JSON.stringify(obj);
            wsParser.parse(this, message);
        },
        sendRaw: function(message, cb) {
            this.cb = cb;
            wsParser.parse(this, message);
        }
    }
}

Now in my mocha test I just do

var wsMock = require("./wsMock.js");
ws = wsMock.createConnectionMock("12345-67890-abcde-fghi-jklmn-opqrs-tuvwxyz");
(...)
describe('Websocket server', function () {

    it('should set sessionId variable after handshake', function (done) {
        ws.send('handshake', {token: data.token}, function (res) {
            var msg = JSON.parse(res);
            msg.action.should.equal('handshake');
            msg.data.should.be.empty;
            ws.should.have.property('sessionId');
            ws.should.not.have.property('session');
            done();
        })
    })

    it('should not return error when making request after handshake', function (done) {
        ws.send('titleAutocomplete', {query: "ter"}, function (res) {
            var msg = JSON.parse(res);
            msg.action.should.equal('titleAutocomplete');
            msg.data.should.be.an.Object;
            ws.should.have.property('session');
            done();
        })
    })
})

It works like a charm and persist connection state and variables between requests.

like image 140
kasztelan Avatar answered Oct 20 '22 17:10

kasztelan