Stubbing WebSocket in JavaScript with Jasmine

≡放荡痞女 提交于 2019-12-30 04:50:05

问题


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.


回答1:


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();
});



回答2:


I came across it trying to mock a websocket for jasmine tests. Here is a solution that uses quite an extensive mock window.WebSocket.

var socketMock;
var windowMock;
var address = 'ws://test.address';

describe(".init(address, window)", function() {
  beforeEach(function() {
    var WebSocket = jasmine.createSpy();
    WebSocket.and.callFake(function (url) {
      socketMock = {
        url: url,
        readyState: WebSocket.CONNECTING,
        send: jasmine.createSpy(),
        close: jasmine.createSpy().and.callFake(function () {
          socketMock.readyState = WebSocket.CLOSING;
        }),

        // methods to mock the internal behaviour of the real WebSocket
        _open: function () {
          socketMock.readyState = WebSocket.OPEN;
          socketMock.onopen && socketMock.onopen();
        },
        _message: function (msg) {
          socketMock.onmessage && socketMock.onmessage({data: msg});
        },
        _error: function () {
          socketMock.readyState = WebSocket.CLOSED;
          socketMock.onerror && socketMock.onerror();
        },
        _close: function () {
          socketMock.readyState = WebSocket.CLOSED;
          socketMock.onclose && socketMock.onclose();
        }
      };
      return socketMock;
    });
    WebSocket.CONNECTING = 0;
    WebSocket.OPEN = 1;
    WebSocket.CLOSING = 2;
    WebSocket.CLOSED = 3;

    windowMock = {
      WebSocket: WebSocket
    };
    spyOn(subject, 'handleMessage');
  });

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

  it("should have onmessage method overriden with a function which handles message", function() {
    var message = 'hello socket';
    subject.init(address, window);

    // pretend the socket connected (optional)
    socketMock._open();

    // pretend the socket got a message
    socketMock._message(message)

    expect(subject.handleMessage).toHaveBeenCalledWith(message);
  });
});


来源:https://stackoverflow.com/questions/23151954/stubbing-websocket-in-javascript-with-jasmine

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!