In this lesson, you will learn how to structure your worker code to facilitate easy dependency injection, enabling you to swap real services for mocks during unit testing.
When building background workers, it is tempting to instantiate heavy dependencies—like database clients or API wrappers—directly inside your worker's task handler. While simple, this makes your code difficult to test because the handler becomes tightly coupled to external infrastructure. To make your code unit-testable in isolation, you must shift toward a factory or "container" pattern.
The Dependency Injection Pattern
Instead of hardcoding a dependency, define your worker task handler as a function that accepts its requirements as arguments. By separating the creation of these dependencies from the execution of the task, you can pass in a mock object during your test suite.
Consider this example of a task handler:
// The task handler is a pure function receiving its dependencies
async function processOrder(orderId, { db, emailService }) {
const order = await db.orders.findById(orderId);
await emailService.send(order.customerEmail, 'Order Processed');
}
// In production, you inject the real services
processOrder('123', { db: realDb, emailService: realEmailService });
// In tests, you inject light-weight mocks
const mockDb = { orders: { findById: jest.fn().mockResolvedValue({ id: '123' }) } };
const mockEmail = { send: jest.fn() };
await processOrder('123', { db: mockDb, emailService: mockEmail });
Why This Works for Workers
By structuring your code this way, you avoid the need to boot up a full database container or mock global state just to verify that your worker logic executes correctly. This approach also simplifies Managing Distributed Locks for Periodic Tasks (Concept 9) or Handling Poison Pills in Workflows (Concept 18), because you can mock the error-handling behavior of your dependencies to simulate system failures and verify your worker's response without actually triggering real side effects.
Treating dependencies as injected parameters transforms your background tasks from monolithic scripts into modular, testable units of business logic that behave predictably, regardless of the environment they run in.
Key Takeaway: Never instantiate your dependencies inside the handler; pass them as arguments so you can swap them for mocks, ensuring your worker logic remains strictly isolated and testable.