Reputation: 7188
I'm having trouble testing my websocket wrapper: data-service is my Angular service to wrap around the native browser WebSocket implementation.
Implementation:
angular.module('core').factory('dataService', function ($interval, webSocket) {
var sock;
function openSocket() {
sock = new webSocket('ws://localhost:9988');
}
function isReady() {
return sock.readyState === 1;
}
openSocket();
$interval(function () {
!isReady() && openSocket();
}, 5000);
});
webSocket is window.WebSocket extracted to an angular constant.
Test:
describe('Data Service', function () {
var dataService,
ws;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000;
beforeEach(function () {
module('core', function ($provide) {
ws = jasmine.createSpy('constructor');
$provide.constant('webSocket', ws);
});
inject(function (_dataService_) {
dataService = _dataService_;
});
});
it('should attempt to connect on load', function () {
expect(ws).toHaveBeenCalled();
});
it('should attempt to reconnect every 5 seconds', function (done) {
setTimeout(function () {
expect(ws.calls.count()).toBe(2);
done();
}, 6000);
});
});
should attempt to connect on load
passes: it was called once as expected.
should attempt to reconnect every 5 seconds
fails: no matter what timeout period I pass to setTimeout
it's only ever called once. I'm wondering if this is due to the socket being re-instantiated every reconnect attempt with the new keyword. I'm not really familiar with how using new in javascript differs to using a normal function to construct an object.
Am I missing something? Or is the browser's WebSocket just a pain to test around?
Upvotes: 1
Views: 1957
Reputation: 27062
A way to avoid the issue with timeout in the test is to make it fully synchronous by calling $interval.flush
to force application time to move forward
it('should attempt to reconnect every 5 seconds', function () {
$interval.flush(4999);
expect(ws.calls.count()).toBe(1);
$interval.flush(1);
expect(ws.calls.count()).toBe(2);
$interval.flush(4999);
expect(ws.calls.count()).toBe(2);
$interval.flush(1);
expect(ws.calls.count()).toBe(3);
});
You can then simulate 10 seconds worth of application time in a few milliseconds of wallclock time. You can see this in action at http://plnkr.co/edit/lz2u08JQLGCgGvX3vKQx
My suspicion as to why the original didn't work, is that the mock implementation of $interval
in the testing environment doesn't actually call the real Javascript setInterval
/setTimeout
at all: it just provides the flush
functionality to move it forward a simulated amount of time.
Upvotes: 2