Jasmine - 使用构造函数

时间:2015-05-10 21:01:08

标签: javascript angularjs unit-testing mocking jasmine

我正在尝试在Angular的jasmine测试中模拟本机WebSocket,我可以监视构造函数和send函数,但我无法弄清楚如何欺骗{{1}的调用}。

WebSocket被提取为Angular常量:onmessage

我的测试看起来像这样:

webSocket

我的代码如下所示:

describe('Data Service', function () {

  var dataService,
    $interval,
    ws;

  beforeEach(function () {
    module('core', function ($provide) {
      ws = jasmine.createSpy('constructor');
      ws.receiveMessage = function (message) {
        this.onmessage(message);
      };
      $provide.constant('webSocket', ws);
    });

    inject(function (_dataService_, _$interval_) {
      dataService = _dataService_;
      $interval = _$interval_;
    });
  });

  it("should call subscribers when a message is received", function () {
    var callback = jasmine.createSpy('onMessage callback');

    function message(type) {
      return {
        data: {
          type: type,
          data: 'data'
        }
      };
    }

    // Subscribe to messages via the exposed function.
    // Subscribe to one of them twice to test that all subscribers are called and not just the first one.
    dataService.onMessage(21, callback);
    dataService.onMessage(21, callback);
    dataService.onMessage(22, callback);
    dataService.onMessage(23, callback);

    // Pick 3 numbers that are valid data types to test.
    ws.receiveMessage(message(21));
    ws.receiveMessage(message(22));
    ws.receiveMessage(message(23));

    expect(callback.calls.count()).toBe(4);
    expect(callback.calls.allArgs()).toBe([message(21).data, message(21).data, message(22).data, message(23).data]);
  });
});

angular.module('core', []).constant('webSocket', WebSocket); angular.module('core').factory('dataService', function ($interval, webSocket) { function openSocket() { sock = new webSocket('ws://localhost:9988'); sock.onmessage = function (message) { var json = JSON.parse(message.data); onMessageSubscribers[json.type].forEach(function (sub) { sub(json); }); }; } function onMessage(type, func) { onMessageSubscribers[type].push(func); } openSocket(); return { onMessage: onMessage }; }); 是一个定义好的数组,其中包含正确的类型(int键),但它与问题无关。

我收到以下错误:

  

TypeError:'undefined'不是函数(评估'this.onmessage(message)')

onMessageSubscribers似乎是在测试之前运行的角度代码定义的,但是我想知道这是否与构造对象与JS中的常规对象的区别有关,我不知道真的对他们有任何经验。

我尝试了几种不同的方法,例如拨打onmessagews.prototype.onmessage

如果我在ws.onmessage中将console.log(sock.onmessage);放置在适当的位置,并在测试中调用dataService之前将另一个日志放入其中:

  

function(message){...}

     

未定义

如何强制调用onmessage或任何其他WebSocket事件?

1 个答案:

答案 0 :(得分:2)

当您致电var sock = new webSocket();时,正在从webSocket构造函数创建新实例。由于您的设计,此sock变量(webSocket实例)未公开,因此在测试套件中不可用。然后,在此onmessage实例上定义属性socksock.onmessage = function () { ... }

您要做的是从测试套件手动触发onmessage,但onmessage附加到sock实例,因为您实际上无法访问{{1} 1}}来自测试套件的实例,因此您也无法访问sock

您甚至无法从原型链中访问它,因为您手边没有实际的onmessage实例。

我想出的是非常棘手的。

JavaScript具有功能 - 您可以从构造函数(info here)显式返回对象,并将其分配给变量而不是新的实例对象。例如:

sock

以下是我们如何使用它来访问function SomeClass() { return { foo: 'bar' }; } var a = new SomeClass(); a; // { foo : 'bar' }; }

onmessage

因此,当您var ws, injectedWs; beforeEach(function () { module('core', function ($provide) { // it will be an explicitly returned object injectedWs = {}; // and it is a constructor - replacer for native WebSocket ws = function () { // return the object explicitly // it will be set to a 'sock' variable return injectedWs; }; $provide.constant('webSocket', ws); }); DataService时,您实际将其分配给返回的{01}},并且它将在我们的测试套件中提供,因为JavaScript另一个功能 - 通过引用传递对象。

最后,您现在可以在需要使用sock.onmessage = function () {};时在测试中致电injectedWs

我已将所有代码移到此处:http://plnkr.co/edit/UIQtLJTyI6sBmAwBpJS7

注意:套件期望和JSON解析存在一些问题,可能是因为您还无法访问此代码,我已修复它们以便测试可以运行。