Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stubbing WebSocket in JavaScript with Jasmine

I try to test if onmessage is a proper function.

Here is a test:

  describe(".init(address, window)", function() {
    beforeEach(function() {
      address = 'ws://test.address';
      window = {};
      e = {
        data: {}
      }
      spyOn(window, 'WebSocket').and.returnValue(function() {return {onmessage: null}});
      spyOn(subject, 'handleMessage');
    });

    it("should create a WebSocket client which connects to the given address", function() {
      subject.init(address, window);
      expect(window.WebSocket).toHaveBeenCalledWith(address);
    });

    it("should have onmessage method overriden with a function which handles message", function() {
      ws = subject.init(address, window);
      alert(JSON.stringify(ws));
      ws.onmessage(e);
      expect(subject.handleMessage).toHaveBeenCalledWith(e.data);
    });
  });

Here is the implementation:

FL.init = function(address, window) {
  if ('WebSocket' in window) {
    var ws = new WebSocket(address);
    ws.onmessage = function(e) {
      this.handleMessage(e.data);
    };
    return ws;
  }
};

The first test passes. In the second, ws is undefined. Why is that? I tried in a console new function() {return {onmessage: null}} and it looks it should be ok.

like image 923
ciembor Avatar asked Apr 18 '14 10:04

ciembor


Video Answer


1 Answers

I'm not completely sure that I figured out how your code is supposed to behave, but if you need to spy on WebSocket constructor and stub .send method to mock some incoming messages, here is how to achieve it.

To spy on WebSocket you will need to call .and.callThrough rather than returnValue. However it result with complains about lack of new keyword (as mentioned in here), so you need to fake the constructor:

var realWS = WebSocket;
var WebSocketSpy = spyOn(window, "WebSocket").and.callFake(function(url,protocols){
  return new realWS(url,protocols);
});

To make a spy for incoming messages you can simply do

var onmessageCallbackSpy = jasmine.createSpy('onmessageCallback');

You can also spy on .send method, and provide some mocked responses with:

var sendSpy = spyOn(WebSocket.prototype, "send").and.callFake(function(outMsg){
  // process/check outgoing message
  // setTimeout, or call it immediately
  this.onmessage("mock message goes here");
}); 

But make sure to use WebSocket.prototype, before you replace it with WebSocketSpy.

So full working example, should look like this:

it("should spy and callFake WebSocket constructor, and stub prototype methods", function (done) {
    var realWS= WebSocket;  
    var sendSpy = spyOn(WebSocket.prototype, "send").and.callFake(function(outMsg){
      if(outMsg == "outgoing message"){
        this.onmessage("incoming mocked message goes here");
      }
    });  
    // var messageSpy = spyOn(WebSocket.prototype, "onmessage");//.and.returnValue("mock message goes here");      
    var WSSpy = spyOn(window, "WebSocket").and.callFake(function(url,protocols){
      return new realWS(url,protocols);
    }); 
    var onmessageCallbackSpy = jasmine.createSpy('onmessageCallback');       

    // Your code
    // (function init(url, onmessageCallbackSpy){
        var ws = new WebSocket("ws://some/where");
        ws.onmessage = onmessageCallbackSpy;
        // code that results with receiving a message
        // or mocked send, that calls `.onmessage` immediately
        ws.send("outgoing message");
    // })();    

    expect(WSSpy).toHaveBeenCalledWith("ws://some/where");
    expect(onmessageCallbackSpy).toHaveBeenCalledWith("mock message goes here");
    done();
});
like image 184
tomalec Avatar answered Sep 19 '22 20:09

tomalec