Jasmine - 使用构造函数正确模拟对象
Jasmine - correctly mocking an object with a constructor
我试图在 Angular 的茉莉花测试中模拟本机 WebSocket,我可以监视构造函数和 send
函数,但我不知道如何欺骗onmessage
.
的调用
WebSocket 被提取到一个 Angular 常量: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
};
});
onMessageSubscribers
是一个已定义的数组,其中包含正确的类型(int 键),但它与问题无关。
我收到以下错误:
TypeError: 'undefined' is not a function (evaluating 'this.onmessage(message)')
onmessage
似乎由测试前运行的 angular 代码定义,但我想知道这是否与构造对象与常规对象的不同之处有关JS,我真的没有任何经验。
我尝试了几种不同的方法来做到这一点,比如调用 ws.prototype.onmessage
或只调用 ws.onmessage
.
如果我在 dataService
中的适当位置放置一个 console.log(sock.onmessage);
,并且在测试中调用 onmessage
之前再记录另一个日志:
function (message) { ... }
undefined
如何强制调用 onmessage 或任何其他 WebSocket 事件?
当您调用 var sock = new webSocket();
时,正在从 webSocket
构造函数创建新实例。由于您的设计,此 sock
变量(webSocket
实例)未公开公开,因此在测试套件中不可用。然后,您在此 sock
实例上定义一个 属性 onmessage
:sock.onmessage = function () { ... }
.
你想做的是从测试套件手动触发 onmessage
,但是 onmessage
附加到 sock
实例,因为你实际上没有访问权限sock
来自测试套件的实例,因此您也无权访问 onmessage
。
您甚至无法从原型链访问它,因为您手头没有实际的 sock
实例。
我想出的东西真的很棘手。
JavaScript 有一个 特征 - 你可以明确地 return 来自构造函数(info here)的对象,它将被分配给一个变量而不是新的实例对象。例如:
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
中执行 sock.onmessage = function () {};
时,您实际上将其分配给了 returned injectedWs
的显式,并且它将在我们的测试套件,因为 JavaScript 另一个 特性 - 对象通过引用传递。
最后,您现在可以在测试中随时调用 onmessage
使用 injectedWs.onmessage(message)
。
我已将所有代码移至此处:http://plnkr.co/edit/UIQtLJTyI6sBmAwBpJS7。
注意:套件预期和 JSON 解析存在一些问题,可能是因为您还无法访问此代码,我已经修复它们以便测试可以 运行。
我试图在 Angular 的茉莉花测试中模拟本机 WebSocket,我可以监视构造函数和 send
函数,但我不知道如何欺骗onmessage
.
WebSocket 被提取到一个 Angular 常量: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
};
});
onMessageSubscribers
是一个已定义的数组,其中包含正确的类型(int 键),但它与问题无关。
我收到以下错误:
TypeError: 'undefined' is not a function (evaluating 'this.onmessage(message)')
onmessage
似乎由测试前运行的 angular 代码定义,但我想知道这是否与构造对象与常规对象的不同之处有关JS,我真的没有任何经验。
我尝试了几种不同的方法来做到这一点,比如调用 ws.prototype.onmessage
或只调用 ws.onmessage
.
如果我在 dataService
中的适当位置放置一个 console.log(sock.onmessage);
,并且在测试中调用 onmessage
之前再记录另一个日志:
function (message) { ... }
undefined
如何强制调用 onmessage 或任何其他 WebSocket 事件?
当您调用 var sock = new webSocket();
时,正在从 webSocket
构造函数创建新实例。由于您的设计,此 sock
变量(webSocket
实例)未公开公开,因此在测试套件中不可用。然后,您在此 sock
实例上定义一个 属性 onmessage
:sock.onmessage = function () { ... }
.
你想做的是从测试套件手动触发 onmessage
,但是 onmessage
附加到 sock
实例,因为你实际上没有访问权限sock
来自测试套件的实例,因此您也无权访问 onmessage
。
您甚至无法从原型链访问它,因为您手头没有实际的 sock
实例。
我想出的东西真的很棘手。
JavaScript 有一个 特征 - 你可以明确地 return 来自构造函数(info here)的对象,它将被分配给一个变量而不是新的实例对象。例如:
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
中执行 sock.onmessage = function () {};
时,您实际上将其分配给了 returned injectedWs
的显式,并且它将在我们的测试套件,因为 JavaScript 另一个 特性 - 对象通过引用传递。
最后,您现在可以在测试中随时调用 onmessage
使用 injectedWs.onmessage(message)
。
我已将所有代码移至此处:http://plnkr.co/edit/UIQtLJTyI6sBmAwBpJS7。
注意:套件预期和 JSON 解析存在一些问题,可能是因为您还无法访问此代码,我已经修复它们以便测试可以 运行。