Taking the example code from https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/basic_usage , the following is run by a web worker
// in worker.js
onmessage = function(e) {
console.log('Message received from main script');
var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
console.log('Posting message back to main script');
postMessage(workerResult);
}
run by code in an Angular service/factory
var myWorker = new Worker("worker.js");
I would like to be able to unit test the code in worker.js
, ideally running it as part of an Angular service/factory (in a separate app running in the web worker?), so I can use the DI system to inject mocks of dependencies, and have the unit test code looking much like the tests for any other service. How can I do this?
There is a way to do this, where the main code run in a web worker is run as part of a separate, manually-bootstrapped, Angular module.
In order to load Angular in a web worker
window
and document
objectsimportScripts
importScripts
Example code that does this:
// worker.js
// Angular needs a global window object
var window = self;
// Skeleton properties to get Angular to load and bootstrap.
self.history = {};
var document = {
readyState: 'complete',
querySelector: function() {},
createElement: function() {
return {
pathname: '',
setAttribute: function() {}
}
}
};
// Load Angular: must be on same domain as this script
importScripts('angular.js');
// Put angular on global scope
angular = window.angular;
// Standard angular module definition
importScripts('worker-app.js');
// No root element seems to work fine
angular.bootstrap(null, ['worker-app']);
The code of the Angular module itself can then be very standard: with services/factories defined as desired. In this case, none are needed and I've opted to put the simple code of the example as a run
callback (I've omitted the console.log
s from the question).
// worker-app.js
(function() {
'use strict';
var app = angular.module('worker-app', []);
app.run(function($window) {
$window.onmessage = function(e) {
var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
$window.postMessage(workerResult);
};
});
})();
This can then be tested using exactly the same tests as if it weren't meant to run in a web worker:
// worker-app-spec.js
describe('worker-app', function() {
'use strict';
var $window;
beforeEach(module('worker-app'));
beforeEach(inject(function(_$window_) {
$window = _$window_;
}));
beforeEach(function() {
spyOn($window, 'postMessage');
})
it('attaches to $window onmessage', function() {
var data = [2,3];
var result = 'Result: ' + (data[0] * data[1]);
$window.onmessage({data: data});
expect($window.postMessage).toHaveBeenCalledWith(result);
});
});
I'm not such a fan of hacking the environment to get Angular to load, as it feels very brittle to me, so other answers very welcome! The above code was tested using AngularJS v1.3.7.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With