Wednesday, January 29, 2014

How To Mock Web Socket Message Events

Web socket connections are perfect for keeping your user interface synchronized with the reality of the backend. There are certainly novel solutions to long-polling to simulate this effect, but it's neither elegant, nor efficient. The trouble with web socket development is that unless you've got the backend chops necessary to keep a mock web socked environment maintained, it's often easier to go the polling route. It's just a matter of the UI developer being able to independently write code. However, there's a way to do web socket development independent of a backend connection, allowing the UI developer to implement real-time updates.

The key use of web sockets is the message event. This is where the UI code responds to changes in backend data. The web socket, a real web socket, is kept open. So discrete updates are "streamed" to the user interface. All we have to do is react to these events, making the appropriate DOM updates. Here's an example that works in Chrome, but not other browsers. The technique is to create an real web socket instance, but with a non-existent URL. This creates the instance in a disconnected state — other browsers don't allow this to happen. But, just because the web socket isn't connected doesn't mean we can't dispatch events. Let's take a look at the code.

function mockSock() {
        
    users.push( "User " + ( users.length + 1 ) );
        
    sock.dispatchEvent( new MessageEvent( "message", {
        data: {
            topic: "user.insert",
            user: _.last( users )
        }
    }));
        
    _.delay( mockSock, 3000 );
        
}

The mockSock() function starts the mocking "process" — an interval that dispatches MessageEvent events. We dispatch the event directly on the web socket instance, and this is enough to trigger our production code that would otherwise respond to real web socket message events. Just be sure to include your message data in the data key, as this is expected by the handler.

sock = new WebSocket( "ws://mock" );
        
sock.onmessage = function( e ) {
    var msg = e.data;
    if ( msg.topic === "user.insert" ) {
        $( "<li/>" ).text( msg.user )
                    .appendTo( "ul" );
    }
};        
        
mockSock();

You can see that the sock instance is created using a bogus URL. This would simply be changed to a real one when ready to test against a real web socket connection. The onmessage code, however, is the real production level code that stays the same, real connection or mocked.