I was stoked to find the Web Worker API small and straightforward, making it super easy to get up and running. The only real speed bump while getting started was the lack of support in older browsers (IE8 and IE9 you ruin everything). However, turns out there is already a polyfill that works great. :) https://code.google.com/p/ie-web-worker/
A long running background worker would be difficult to properly unit test, but my case was a bit simpler. I was posting data to the worker and letting it spit a result back out. I decided to just try the simplest Jasmine test first:
Note: This is just an example with a similar structure as my actual app.
Surprising to me, this didn’t work! :( The test seemed to pass, but there was a Jasmine error.
What seemed to be going on is that, since this is an asynchronous test, by the time the execution of the test reached the expectation, the jasmine environment was no longer valid or able to perform the assertion.
So in the process of writing this I discovered a more correct solution to my problem, which I have included here. But I decided to keep around the whole post because of the Rails intricacies and my overall problem solving thought process.
Turns out that Jasmine already has support for these type of asynchronous operations with the use of a
done() function, that Jasmine will use to know when an asynchronous test has finished.
This is the solution I will be going with, but if you keep reading you will see something I came up with that uses promises to place the assertion AFTER “postMessage,” which I find easier to read and reason about when doing asynchronous tests.
Lesson learned here: always read the documentation fully and upgrade if you can first.
Time to be clever. Since my goal was to test the Web Worker code itself, I decided to reverse engineer the Web Worker API. I realized that the Worker was making an XMLHttpRequest to grab the script and then executing the code in its own context, so I took a similar strategy:
Now that I had a working solution, I had to write more tests for more workers (so far my app has 14 workers and maybe more to come), which means reusability. I wanted to extract away all the hairiness of requesting the worker script and evaling it into the current context. I also don’t like writing the expectation before the action of the test, so I turned to promises to help out.
Note: using jQuery’s Deferred here as my promise library because I already have jQuery in the project.
Bingo! I was pretty happy with the final solution. It made testing of the rest of the workers trivial. And it works both in the browser and in a headless environment such as phantomjs.
I am using Rails 4 on the backend for this, which actually took a bit of time to get everything set up to work correctly in a Rails pipeline. Here is what I’ve done.
- “application.js” does NOT require the workers. As I alluded to, when instantiating a worker with
new Worker(‘script_name.js’), an AJAX request is made to the server to fetch the resource, so compiling it into application.js isn’t necessary
- Add all workers to the precompile array:
The final issue was with jasmine_rails. Running
So a few hoops to jump through, but now I’m very happy with the Web Workers and the testing strategy I arrived at.