Testing Utilities
Mongodash provides a set of utilities to help you write robust End-to-End (E2E) tests for your asynchronous applications.
waitUntilReactiveTasksIdle
A specialized helper for testing Reactive Tasks. It blocks execution until the reactive task system is completely quiesced.
This is essential for testing "side effects" and "cascading tasks" where you want to assert the final state of the system after a chain of events has completed.
What it waits for
It resolves only when ALL of the following validation checks pass simultaneously (or for the stabilityDurationMs):
- Planner Empty: The internal
ReactiveTaskPlannerhas no buffered Change Stream events waiting to be flushed. - Workers Idle: No
ResponsiveTaskWorkeris currently processing a task (active count is 0). - Database Settled: No tasks in any registered task collection are in
pending,processing, orprocessing_dirtystate.- Exception: Pending tasks scheduled for the distant future (beyond the current timeout + stability window) are ignored. This ensures that long-term retries (e.g., "retry in 1 hour") do not cause your tests to timeout.
Usage
import { waitUntilReactiveTasksIdle } from 'mongodash/testing';
it('should process user registration workflow', async () => {
// 1. Trigger the workflow
await users.insertOne({ email: 'test@example.com', status: 'new' });
// 2. Wait for the reactive task to process ONLY the insert
// AND any subsequent tasks that might be triggered (e.g. sending email)
await waitUntilReactiveTasksIdle();
// 3. Assert the final state
const emailTask = await emailTasks.findOne({ email: 'test@example.com' });
expect(emailTask).toBeDefined();
expect(emailTask.status).toBe('sent');
});Configuration
You can override the default options if needed, though the defaults are tuned for general use.
await waitUntilReactiveTasksIdle({
timeoutMs: 30000,
stabilityDurationMs: 200, // Wait for 200ms of "silence" to catch in-flight cascading tasks
});Isolation with Whitelist
When running parallel tests, you can use the whitelist option to wait only for specific tasks/collections:
await waitUntilReactiveTasksIdle({
whitelist: [
{ collection: 'users', filter: { _id: userId } }, // Wait for specific user
{ collection: 'orders', task: 'processOrder' }, // Wait for specific task
]
});
## `assertNoReactiveTaskErrors`
Ensures that no reactive tasks have failed during your test execution. This is critical for catching "silent failures" where a task failed but didn't crash the application or the test.
### Features
- **Time filtering**: Only checks for errors that occurred after a specific time (e.g., the start of your test).
- **Scope**: Can check globally or for specific source documents.
- **Filtering**: Allows ignoring "expected" errors (e.g., negative tests).
### Usage
```typescript
import { assertNoReactiveTaskErrors } from 'mongodash/testing';
it('should process successfully', async () => {
const startTime = new Date();
// ... run test steps ...
await waitUntilReactiveTasksIdle();
// Verify no tasks failed
await assertNoReactiveTaskErrors({ since: startTime });
});Options
await assertNoReactiveTaskErrors({
since: startTime, // Required: Check for errors after this time
// Optional: Only check specific tasks (ignore background noise)
whitelist: [{
collection: 'users',
filter: { _id: userId }
}],
excludeErrors: [ // Optional: Allow known errors (strings or RegEx)
'Expected Failure',
/Authorization Error/
]
});configureForTesting
A helper to configure the library for fast, deterministic testing execution.
By default, the library is optimized for production scenarios (e.g. debounce: 1000ms, minPollMs: 200ms). In tests, these delays cause unnecessary slowness.
configureForTesting overrides these defaults globally to minimal values (e.g. 10ms).
Usage
Call this once in your global test setup (e.g. jest.setup.js or beforeAll).
It works regardless of whether you call it before or after registering your tasks.
import { configureForTesting } from 'mongodash/testing';
beforeAll(() => {
// Sets debounce, polling, and batching to 10ms
configureForTesting();
});Options
You can customize specific values if needed:
configureForTesting({
debounce: 0, // Force 0ms debounce (immediate execution)
minPollMs: 50, // Poll every 50ms
minBatchIntervalMs: 50
});