我正在尝试在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中的常规对象的区别有关,我不知道真的对他们有任何经验。
我尝试了几种不同的方法,例如拨打onmessage
或ws.prototype.onmessage
。
如果我在ws.onmessage
中将console.log(sock.onmessage);
放置在适当的位置,并在测试中调用dataService
之前将另一个日志放入其中:
function(message){...}
未定义
如何强制调用onmessage或任何其他WebSocket事件?
答案 0 :(得分:2)
当您致电var sock = new webSocket();
时,正在从webSocket
构造函数创建新实例。由于您的设计,此sock
变量(webSocket
实例)未公开,因此在测试套件中不可用。然后,在此onmessage
实例上定义属性sock
:sock.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解析存在一些问题,可能是因为您还无法访问此代码,我已修复它们以便测试可以运行。