loading...

How to test Web Workers with Jest

astagi profile image Andrea Stagi Originally published at vuedose.tips ・2 min read

As you could read in Alex's article, Use Web Workers in your Vue.js Components for Max Performance, you can use Web Workers to maximize performance in your Vue.js app instead of running heavy tasks in the main thread which is UI-blocking. But how can we test Web Workers? Webpack-bundled Web Workers are not supported by Jest so we have to mock the worker in order to test it! Let's see how to do in 3 simple steps, starting from a simple Vue app to calculate the Fibonacci number, where fibonacci function is the heavy task performed by the Web worker (you can follow the code here)

First of all we need to isolate the main functionality of our worker, in this case is really straightforward because it's just our fibonacci function (src/fibonacci.js)

let fibonacci = (num) => {
  if (num <= 1) return 1;
  return fibonacci(num - 1) + fibonacci(num - 2);
}

export default fibonacci
Enter fullscreen mode Exit fullscreen mode

and keep the worker minimal (src/fibonacci.worker.js):

import fibonacci from "./fibonacci";

self.onmessage = async function (e) {
  self.postMessage(fibonacci(e.data));
};
Enter fullscreen mode Exit fullscreen mode

This way we can mock just the Web Worker part of our implementation (src/__mocks__/fibonacci.worker.js)

import fibonacci from "../fibonacci";

export default class fibonacciWorker {
  constructor() {
    // Note that `onmessage` should be overwritten by the code using the worker.
    this.onmessage = () => { };
  }

  postMessage(data) {
    this.onmessage({ data: fibonacci(data) });
  }
}
Enter fullscreen mode Exit fullscreen mode

and easily test the main functionality

import { shallowMount } from '@vue/test-utils'
import App from '@/App.vue'

jest.mock("@/fibonacci.worker")

describe('Fibonacci App.vue', () => {
  it('should calculate Fibonacci number', async () => {
    const wrapper = shallowMount(App)
    await wrapper.find('input').setValue('10')
    await wrapper.find('button').trigger('click')
    expect(wrapper.find('.result').element.innerHTML).toBe('Result: 89')
  })
})
Enter fullscreen mode Exit fullscreen mode

I created workerloader-jest-transformer to generalize this solution so that all workers are mocked at once. This Jest transformer helps you to test Web Workers loaded with Webpack worker-loader module in Jest. It's easy to use, install it with

yarn add workerloader-jest-transformer --dev
Enter fullscreen mode Exit fullscreen mode

and add the tranformation rule to your Jest configuration:

transform: {
  "^.+\\.worker.[t|j]sx?$": "workerloader-jest-transformer"
}
Enter fullscreen mode Exit fullscreen mode

This transformer is inspired by jsdom-worker and implements Web Worker API for JSDOM, so you can remove any mocking code as you can see here.

Workerloader-jest-transformer is highly experimental and code is available on Github, any contribution and advice would be greatly appreciated!

Discussion

pic
Editor guide