0
# Test Scheduling
1
2
Jest's test scheduling system manages test execution coordination, multi-process scheduling, reporter management, and comprehensive result aggregation for optimal performance and reporting.
3
4
## Capabilities
5
6
### TestScheduler Class
7
8
The TestScheduler manages test execution scheduling and coordinates with reporters for comprehensive test result processing.
9
10
```typescript { .api }
11
/**
12
* Manages test execution scheduling and reporting
13
*/
14
class TestScheduler {
15
constructor(globalConfig: Config.GlobalConfig, context: TestSchedulerContext);
16
17
/**
18
* Adds a reporter to the dispatcher
19
* @param reporter - Reporter instance to add
20
*/
21
addReporter(reporter: Reporter): void;
22
23
/**
24
* Removes a reporter from the dispatcher
25
* @param reporterConstructor - Constructor of reporter to remove
26
*/
27
removeReporter(reporterConstructor: ReporterConstructor): void;
28
29
/**
30
* Schedules and executes the given tests
31
* @param tests - Array of test files to execute
32
* @param watcher - Test watcher for watch mode integration
33
* @returns Promise resolving to aggregated test results
34
*/
35
scheduleTests(tests: Array<Test>, watcher: TestWatcher): Promise<AggregatedResult>;
36
}
37
38
/**
39
* Factory function that creates a TestScheduler and sets up reporters
40
* @param globalConfig - Global Jest configuration
41
* @param context - Scheduler context including reporter and test runner contexts
42
* @returns Promise resolving to configured test scheduler
43
*/
44
function createTestScheduler(
45
globalConfig: Config.GlobalConfig,
46
context: TestSchedulerContext
47
): Promise<TestScheduler>;
48
49
type TestSchedulerContext = ReporterContext & TestRunnerContext;
50
51
type ReporterConstructor = new (
52
globalConfig: Config.GlobalConfig,
53
reporterConfig: Record<string, unknown>,
54
reporterContext: ReporterContext,
55
) => JestReporter;
56
```
57
58
**Usage Examples:**
59
60
```typescript
61
import { createTestScheduler, SearchSource } from "jest";
62
63
// Create and configure test scheduler
64
async function setupTestScheduler() {
65
const scheduler = await createTestScheduler(globalConfig, {
66
...reporterContext,
67
...testRunnerContext
68
});
69
70
// Add custom reporter
71
scheduler.addReporter(new CustomReporter(globalConfig, {}, reporterContext));
72
73
return scheduler;
74
}
75
76
// Execute tests with scheduler
77
async function executeTests(testFiles: Array<Test>) {
78
const scheduler = await setupTestScheduler();
79
80
const results = await scheduler.scheduleTests(
81
testFiles,
82
new TestWatcher({ isWatchMode: false })
83
);
84
85
return results;
86
}
87
```
88
89
### Test Execution Coordination
90
91
The TestScheduler coordinates test execution across multiple processes and manages the complete test lifecycle.
92
93
**Basic Test Scheduling:**
94
95
```typescript
96
import { createTestScheduler, SearchSource } from "jest";
97
98
async function coordinateTestExecution() {
99
// 1. Discover tests
100
const searchSource = new SearchSource(testContext);
101
const searchResult = await searchSource.getTestPaths(
102
globalConfig,
103
projectConfig
104
);
105
106
// 2. Create scheduler
107
const scheduler = await createTestScheduler(globalConfig, schedulerContext);
108
109
// 3. Schedule and execute tests
110
const results = await scheduler.scheduleTests(
111
searchResult.tests,
112
new TestWatcher({ isWatchMode: false })
113
);
114
115
console.log(`Executed ${results.numTotalTests} tests`);
116
console.log(`Passed: ${results.numPassedTests}`);
117
console.log(`Failed: ${results.numFailedTests}`);
118
119
return results;
120
}
121
```
122
123
### Reporter Management
124
125
Manage test result reporting through dynamic reporter configuration.
126
127
```typescript
128
import { createTestScheduler } from "jest";
129
130
// Custom reporter for specialized output
131
class CustomReporter {
132
constructor(
133
private globalConfig: Config.GlobalConfig,
134
private options: Record<string, unknown>,
135
private context: ReporterContext
136
) {}
137
138
onRunStart(results: AggregatedResult, options: ReporterOnStartOptions) {
139
console.log("Starting test run...");
140
}
141
142
onTestResult(test: Test, testResult: TestResult, results: AggregatedResult) {
143
if (testResult.testResults.some(result => result.status === "failed")) {
144
console.log(`❌ ${test.path}`);
145
} else {
146
console.log(`✅ ${test.path}`);
147
}
148
}
149
150
onRunComplete(contexts: Set<TestContext>, results: AggregatedResult) {
151
console.log(`Test run completed: ${results.success ? "PASSED" : "FAILED"}`);
152
}
153
}
154
155
// Configure scheduler with custom reporters
156
async function setupCustomReporting() {
157
const scheduler = await createTestScheduler(globalConfig, schedulerContext);
158
159
// Add multiple reporters
160
scheduler.addReporter(new CustomReporter(globalConfig, {}, reporterContext));
161
scheduler.addReporter(new JSONReporter(globalConfig, { outputFile: "results.json" }, reporterContext));
162
163
// Remove default reporter if needed
164
scheduler.removeReporter(DefaultReporter);
165
166
return scheduler;
167
}
168
```
169
170
### Multi-Process Coordination
171
172
Handle test execution across multiple worker processes for optimal performance.
173
174
```typescript
175
import { createTestScheduler } from "jest";
176
177
async function scheduleTestsWithWorkers(maxWorkers: number) {
178
const globalConfig = {
179
...baseGlobalConfig,
180
maxWorkers,
181
runInBand: maxWorkers === 1
182
};
183
184
const scheduler = await createTestScheduler(globalConfig, schedulerContext);
185
186
// Configure for multi-process execution
187
const results = await scheduler.scheduleTests(
188
testFiles,
189
new TestWatcher({
190
isWatchMode: false,
191
// Additional options for worker coordination
192
})
193
);
194
195
return results;
196
}
197
198
// Adaptive worker configuration
199
async function adaptiveTestScheduling(testCount: number) {
200
// Determine optimal worker count based on test count and system resources
201
const maxWorkers = Math.min(
202
Math.max(1, Math.floor(testCount / 10)), // At least 10 tests per worker
203
require("os").cpus().length, // Don't exceed CPU count
204
8 // Cap at 8 workers
205
);
206
207
return scheduleTestsWithWorkers(maxWorkers);
208
}
209
```
210
211
### Watch Mode Integration
212
213
Integrate with Jest's watch mode for automatic test re-execution.
214
215
```typescript
216
import { createTestScheduler } from "jest";
217
import { TestWatcher } from "jest-watcher";
218
219
async function scheduleTestsInWatchMode() {
220
const scheduler = await createTestScheduler(
221
{ ...globalConfig, watch: true },
222
schedulerContext
223
);
224
225
const watcher = new TestWatcher({ isWatchMode: true });
226
227
// Watch mode provides automatic re-scheduling
228
const results = await scheduler.scheduleTests(testFiles, watcher);
229
230
// In watch mode, this promise typically never resolves
231
// as Jest continues watching for file changes
232
return results;
233
}
234
235
// Custom watch mode logic
236
class CustomTestWatcher extends TestWatcher {
237
constructor(options: { isWatchMode: boolean }) {
238
super(options);
239
}
240
241
async onChange(changedFiles: Set<string>) {
242
console.log(`Files changed: ${Array.from(changedFiles).join(", ")}`);
243
244
// Custom logic for determining which tests to re-run
245
const searchSource = new SearchSource(testContext);
246
const relatedTests = await searchSource.findRelatedTests(changedFiles, false);
247
248
// Re-schedule only related tests
249
if (relatedTests.tests.length > 0) {
250
await this.scheduler.scheduleTests(relatedTests.tests, this);
251
}
252
}
253
}
254
```
255
256
### Result Aggregation and Processing
257
258
Process and aggregate test results for comprehensive reporting.
259
260
```typescript
261
import { createTestScheduler } from "jest";
262
263
interface TestExecutionMetrics {
264
totalDuration: number;
265
averageTestDuration: number;
266
slowestTests: Array<{ path: string; duration: number }>;
267
fastestTests: Array<{ path: string; duration: number }>;
268
failureRate: number;
269
coveragePercentage?: number;
270
}
271
272
async function executeWithMetrics(tests: Array<Test>): Promise<{
273
results: AggregatedResult;
274
metrics: TestExecutionMetrics;
275
}> {
276
const scheduler = await createTestScheduler(globalConfig, schedulerContext);
277
278
const startTime = Date.now();
279
const results = await scheduler.scheduleTests(
280
tests,
281
new TestWatcher({ isWatchMode: false })
282
);
283
const totalDuration = Date.now() - startTime;
284
285
// Calculate metrics
286
const testDurations = results.testResults
287
.flatMap(suite => suite.testResults)
288
.map(test => ({ path: test.title, duration: test.duration || 0 }))
289
.filter(test => test.duration > 0);
290
291
const averageTestDuration = testDurations.length > 0
292
? testDurations.reduce((sum, test) => sum + test.duration, 0) / testDurations.length
293
: 0;
294
295
const sortedByDuration = testDurations.sort((a, b) => b.duration - a.duration);
296
297
const metrics: TestExecutionMetrics = {
298
totalDuration,
299
averageTestDuration,
300
slowestTests: sortedByDuration.slice(0, 5),
301
fastestTests: sortedByDuration.slice(-5).reverse(),
302
failureRate: results.numTotalTests > 0
303
? results.numFailedTests / results.numTotalTests
304
: 0,
305
coveragePercentage: results.coverageMap
306
? calculateCoveragePercentage(results.coverageMap)
307
: undefined
308
};
309
310
return { results, metrics };
311
}
312
313
function calculateCoveragePercentage(coverageMap: any): number {
314
// Implementation would depend on coverage map structure
315
// This is a simplified example
316
const summary = coverageMap.getCoverageSummary?.();
317
return summary?.lines?.pct || 0;
318
}
319
```
320
321
### Error Handling and Recovery
322
323
Implement robust error handling for test scheduling failures.
324
325
```typescript
326
import { createTestScheduler } from "jest";
327
328
async function robustTestScheduling(tests: Array<Test>) {
329
let scheduler: TestScheduler;
330
331
try {
332
scheduler = await createTestScheduler(globalConfig, schedulerContext);
333
} catch (error) {
334
console.error("Failed to create test scheduler:", error);
335
throw new Error("Test scheduler initialization failed");
336
}
337
338
// Add error reporter
339
scheduler.addReporter(new ErrorTrackingReporter());
340
341
try {
342
const results = await scheduler.scheduleTests(
343
tests,
344
new TestWatcher({ isWatchMode: false })
345
);
346
347
// Check for critical failures
348
if (results.numRuntimeErrorTestSuites > 0) {
349
console.warn(`${results.numRuntimeErrorTestSuites} test suites had runtime errors`);
350
}
351
352
// Handle open handles
353
if (results.openHandles && results.openHandles.length > 0) {
354
console.warn(`${results.openHandles.length} open handles detected`);
355
356
// Optionally force exit
357
if (globalConfig.forceExit) {
358
process.exit(results.success ? 0 : 1);
359
}
360
}
361
362
return results;
363
364
} catch (error) {
365
console.error("Test execution failed:", error);
366
367
// Attempt recovery or cleanup
368
if (error.message.includes("worker")) {
369
console.log("Retrying with single worker...");
370
const fallbackConfig = { ...globalConfig, maxWorkers: 1, runInBand: true };
371
const fallbackScheduler = await createTestScheduler(fallbackConfig, schedulerContext);
372
return fallbackScheduler.scheduleTests(tests, new TestWatcher({ isWatchMode: false }));
373
}
374
375
throw error;
376
}
377
}
378
379
class ErrorTrackingReporter {
380
private errors: Array<{ test: string; error: any }> = [];
381
382
onTestResult(test: Test, testResult: TestResult) {
383
testResult.testResults.forEach(result => {
384
if (result.status === "failed") {
385
this.errors.push({
386
test: `${test.path} > ${result.title}`,
387
error: result.failureMessages
388
});
389
}
390
});
391
}
392
393
onRunComplete() {
394
if (this.errors.length > 0) {
395
console.log("\n=== Test Failures Summary ===");
396
this.errors.forEach(({ test, error }) => {
397
console.log(`\n❌ ${test}`);
398
console.log(error.join("\n"));
399
});
400
}
401
}
402
}
403
```
404
405
### Performance Optimization
406
407
Optimize test scheduling for different scenarios and constraints.
408
409
```typescript
410
import { createTestScheduler } from "jest";
411
412
interface SchedulingStrategy {
413
name: string;
414
configure: (config: Config.GlobalConfig) => Config.GlobalConfig;
415
}
416
417
const schedulingStrategies: Record<string, SchedulingStrategy> = {
418
fast: {
419
name: "Fast Execution",
420
configure: (config) => ({
421
...config,
422
maxWorkers: "100%",
423
cache: true,
424
bail: 1 // Stop on first failure
425
})
426
},
427
428
thorough: {
429
name: "Thorough Testing",
430
configure: (config) => ({
431
...config,
432
maxWorkers: "50%",
433
collectCoverage: true,
434
bail: false
435
})
436
},
437
438
debug: {
439
name: "Debug Mode",
440
configure: (config) => ({
441
...config,
442
maxWorkers: 1,
443
runInBand: true,
444
verbose: true,
445
detectOpenHandles: true
446
})
447
},
448
449
ci: {
450
name: "CI Optimized",
451
configure: (config) => ({
452
...config,
453
ci: true,
454
maxWorkers: "50%",
455
cache: false,
456
collectCoverage: true,
457
coverageReporters: ["text", "lcov"]
458
})
459
}
460
};
461
462
async function scheduleWithStrategy(
463
tests: Array<Test>,
464
strategyName: keyof typeof schedulingStrategies
465
) {
466
const strategy = schedulingStrategies[strategyName];
467
const optimizedConfig = strategy.configure(globalConfig);
468
469
console.log(`Using ${strategy.name} strategy`);
470
471
const scheduler = await createTestScheduler(optimizedConfig, schedulerContext);
472
return scheduler.scheduleTests(
473
tests,
474
new TestWatcher({ isWatchMode: false })
475
);
476
}
477
478
// Auto-select strategy based on environment
479
async function smartScheduling(tests: Array<Test>) {
480
const isCI = process.env.CI === "true";
481
const isDebug = process.env.DEBUG === "true";
482
const testCount = tests.length;
483
484
let strategy: keyof typeof schedulingStrategies;
485
486
if (isDebug) {
487
strategy = "debug";
488
} else if (isCI) {
489
strategy = "ci";
490
} else if (testCount < 10) {
491
strategy = "fast";
492
} else {
493
strategy = "thorough";
494
}
495
496
return scheduleWithStrategy(tests, strategy);
497
}
498
```
499
500
Jest's test scheduling system provides complete control over test execution coordination, enabling optimized performance, comprehensive reporting, and reliable test execution across different environments and use cases.