Testing Worker Threads with Jest
Worker threads allow you to run JavaScript code in parallel, improving performance for CPU-intensive tasks. This challenge focuses on writing Jest tests to ensure your worker thread implementation functions correctly, handling potential errors and verifying data transfer between the main thread and the worker thread. Successfully testing worker threads requires careful consideration of asynchronous operations and thread lifecycle management.
Problem Description
You are tasked with creating a Jest test suite for a module that utilizes worker threads. The module, workerThreadModule.ts, contains a function processData which spawns a worker thread to perform a computationally expensive operation (simulated by a simple calculation). The processData function takes an array of numbers as input, and the worker thread calculates the sum of squares of each number in the array. The worker thread then sends the result back to the main thread, which returns the result.
Your goal is to write Jest tests that:
- Verify that
processDatacorrectly calculates the sum of squares for a given array of numbers. - Handle potential errors that might occur within the worker thread (e.g., an uncaught exception). The
processDatafunction should catch errors from the worker thread and re-throw them in the main thread. - Ensure that the worker thread is properly terminated after completing its task or encountering an error.
- Test with an empty input array.
Examples
Example 1:
Input: [1, 2, 3]
Output: 14
Explanation: 1*1 + 2*2 + 3*3 = 1 + 4 + 9 = 14
Example 2:
Input: [2, 4, 6]
Output: 56
Explanation: 2*2 + 4*4 + 6*6 = 4 + 16 + 36 = 56
Example 3: (Edge Case - Empty Array)
Input: []
Output: 0
Explanation: The sum of squares of an empty array is 0.
Example 4: (Error Handling)
Input: [1, 2, "a"] // Simulate an error in the worker thread
Output: TypeError: Cannot read properties of undefined (reading 'toString')
Explanation: The worker thread should throw an error when encountering invalid input, and this error should be propagated to the main thread and re-thrown.
Constraints
- The
processDatafunction should return a Promise that resolves with the sum of squares or rejects with an error. - The worker thread should be terminated after completing its task or encountering an error.
- The tests should be written using Jest and TypeScript.
- The
workerThreadModule.tsfile will be provided (see below). - The tests should be robust and cover various scenarios, including valid input, invalid input, and error handling.
- Performance is not a primary concern for this challenge; focus on correctness and test coverage.
Notes
- You will need to use
jest.fn()to mock theworkerobject and its methods to control the behavior of the worker thread during testing. - Consider using
async/awaitto simplify asynchronous testing. - Pay close attention to error handling and ensure that errors are correctly propagated from the worker thread to the main thread and caught by the Jest tests.
- The
workerThreadModule.tsfile is provided below. You should not modify this file. Your solution should be a Jest test suite that tests this module.
// workerThreadModule.ts (DO NOT MODIFY)
import { Worker } from 'worker_threads';
export function processData(data: number[]): Promise<number> {
return new Promise((resolve, reject) => {
const worker = new Worker(`
const { parentPort } = require('worker_threads');
parentPort.on('message', (data) => {
try {
const sumOfSquares = data.reduce((sum, num) => sum + num * num, 0);
parentPort.postMessage(sumOfSquares);
} catch (error) {
parentPort.postMessage({ error: error.message });
}
});
`, { eval: true });
worker.postMessage(data);
worker.on('message', (message: any) => {
if (message.error) {
reject(new Error(message.error));
} else {
resolve(message);
}
worker.terminate();
});
worker.on('error', (error) => {
reject(error);
worker.terminate();
});
worker.on('exit', (code) => {
if (code !== 0) {
reject(new Error(\`Worker stopped with exit code \${code}\`));
}
});
});
}