Sinon: Add support for WebSockets

Created on 5 Sep 2012  路  18Comments  路  Source: sinonjs/sinon

I have a project (and a few coworkers have projects also) that use WebSockets, and we'd like to be able to test them using jasmine and sinon in a similar manner to how we can test normal ajax.

1.x Feature Request

Most helpful comment

+1 (or at least some entry in the docs showing how it could be done :))

All 18 comments

You can test WebSockets by stubbing the native WS functions.
How should additional functionality behave?

similarly to sinon fakeServer (in fact, perhaps it should be part of fakeServer)

maybe something like this:

var serverSocket = null;

server.respondWith(/^open_socket/, function (xhr) {
  serverSocket = xhr.webSocket;
  xhr.upgradeWebSocket(); 
  serverSocket.respond("a message");
});

serverSocket.respond("another message");

setTimeout(function () {
  serverSocket.respond("yet another message");
  serverSocket.close();
}, 5000);

+1

:+1:

+1 (or at least some entry in the docs showing how it could be done :))

Would be nice to have that feature. I can't figure out how to stub my socket-functions.

For the time being, you could try something like this (I'm making this up without actual testing, so please forgive me if it doesn't work):

// Test setup:
var dummySocket = { send : sinon.spy() };
sinon.stub(window, 'WebSocket').returns(dummySocket);

// Your logic to initiate the WS connection goes here

// These functions should have been added by your code, we can just call them:
dummySocket.onopen();
dummySocket.onmessage(JSON.stringify({ hello : 'from server' }));

// You can assert whether your code sent something to the server like this:
sinon.assert.calledWith(dummySocket.send, '{"the client":"says hi"}');

Thoughts?

Hi, Thanx for your response.
Hm, i don't get it:
dummySocket.send.called is false.

I don't understand dummySocket.onopen();, while TypeError: Object # has no method 'onopen'.

I was just assuming that your WebSocket is initiated somehow like described here:
http://docs.webplatform.org/wiki/concepts/protocols/websocket

Sounds like you did not register an onopen callback.

Ah, a fu in my hierarchy. I am using socket.io and emit an event "my::id". But this Code in question is a extend from a singleton. Anyway its never coming to the event if i am not loged in and the event will be called before the spy can get it. (RequireJS Extends, Singletons. Ahhh.). I refactor my code and add a route that the user is always logged in development-test mode. This is the best solution because i always register against an external system that i don't want to test. And a clean code is what i need now. The socket should be open allready for the function that i write. But it issn't if i am not logged in.
But than, your solution should word with socket.io, right?
Thanks again.

I use socket.io as well, but my test cases don't go down to the actual transport implementation.

My setup code looks something like this:

var socket = new io.EventEmitter();
sinon.stub(io, 'connect').returns(socket);
// initialization that causes io.connect to be invoked
socket.emit('connect');

You can then emit and listen to events on socket.

Hi everyone! Before anything, I would like to apologise for commenting on such an old issue, but I think I found a problem when executing @mantoni's snippet on Safari.

When calling simon.stub with window and 'WebSocket' as arguments, the following exception is thrown:

TypeError: Attempted to wrap object property WebSocket as function
checkWrappedMethod@sinon/pkg/sinon.js:1221:42
wrapMethod@sinon/pkg/sinon.js:1265:39
stub@sinon/pkg/sinon.js:3165:36

I believe this is due to the way Safari implements WebSocket. The following snippet was run on Safari 8.0.4:

WebSocket.prototype.constructor
=> WebSocketConstructor
WebSocket
=> WebSocketConstructor
typeof WebSocket
=> "object"
typeof WebSocket.prototype.constructor
=> "object"

Which causes the first assertion of the isFunction function fail (typeof obj === "function").
On Chrome, for instance, this does not happen:

WebSocket.prototype.constructor
=> function WebSocket() { [native code] }
WebSocket
=> function WebSocket() { [native code] }
typeof WebSocket
=> "function"
typeof WebSocket.prototype.constructor
=> "function"

I am not sure if there is a way of getting around this, hence this comment.

+1

https://github.com/thoov/mock-socket - alternative implementation.

I did some unit testing with the websocket and sinon modules here: https://github.com/mantoni/licy-websocket.js/blob/master/test/ws-test.js

mock-socket is a mature library with a clean api, recommended.

i hope this will be helpfull:

// chat.js
const WebSocket = require('ws');

function Chat() {
  const chatSocket = new WebSocket('ws://localhost:8080');
  this.messages = [];

  chatSocket.onmessage = (event) => {
    this.messages.push(event.data);
  };
}

module.exports = Chat;

// chat.test.js
const test = require('ava');
const WebSocket = require('ws');
const Chat = require('./chat');

test.cb('basic test', (t) => {
  const mockServer = new WebSocket.Server({ port: 8080 });
  mockServer.on('connection', function (ws) {
    ws.send('test message 1');
    ws.send('test message 2');
  });

  var chatApp = new Chat();

  setTimeout(() => {
    const messageLen = chatApp.messages.length;
    t.true(messageLen == 2, '2 messages where sent from the s server');
    t.end();
  }, 100);
});

I added syntax highlighting to the comment above

Was this page helpful?
0 / 5 - 0 ratings