0
# Testing Utilities
1
2
Comprehensive testing framework with fakeAsync, async testing zones, and task tracking for testing asynchronous code in a controlled environment.
3
4
## Capabilities
5
6
### FakeAsync Testing
7
8
Testing utilities that allow control of time and microtask execution for deterministic async testing.
9
10
```typescript { .api }
11
/**
12
* Create a fakeAsync test function that controls time
13
* @param fn - Test function to wrap
14
* @param options - Configuration options
15
* @returns Wrapped test function
16
*/
17
function fakeAsync(fn: Function, options?: {flush?: boolean}): (...args: any[]) => any;
18
19
/**
20
* Advance virtual time by specified milliseconds
21
* @param millis - Milliseconds to advance (default: 0)
22
* @param ignoreNestedTimeout - Whether to ignore nested setTimeout calls
23
*/
24
function tick(millis?: number, ignoreNestedTimeout?: boolean): void;
25
26
/**
27
* Flush all pending macrotasks and microtasks
28
* @param maxTurns - Maximum number of task execution cycles
29
* @returns Number of tasks flushed
30
*/
31
function flush(maxTurns?: number): number;
32
33
/**
34
* Flush only pending microtasks
35
*/
36
function flushMicrotasks(): void;
37
38
/**
39
* Discard all pending periodic tasks (setInterval)
40
*/
41
function discardPeriodicTasks(): void;
42
43
/**
44
* Reset the fakeAsync zone to initial state
45
*/
46
function resetFakeAsyncZone(): void;
47
48
/**
49
* Wrap a function to automatically use ProxyZoneSpec for test isolation
50
* @param fn - Function to wrap with proxy zone behavior
51
* @returns Wrapped function that runs in proxy zone context
52
*/
53
function withProxyZone<T extends Function>(fn: T): T;
54
```
55
56
**Usage Examples:**
57
58
```typescript
59
import 'zone.js/testing';
60
61
describe('FakeAsync Tests', () => {
62
it('should control time', fakeAsync(() => {
63
let executed = false;
64
65
setTimeout(() => {
66
executed = true;
67
}, 1000);
68
69
// Time hasn't advanced yet
70
expect(executed).toBe(false);
71
72
// Advance time by 1000ms
73
tick(1000);
74
75
// Now the timeout has executed
76
expect(executed).toBe(true);
77
}));
78
79
it('should handle promises', fakeAsync(() => {
80
let result = '';
81
82
Promise.resolve('async-value')
83
.then(value => result = value);
84
85
// Promise hasn't resolved yet
86
expect(result).toBe('');
87
88
// Flush microtasks
89
flushMicrotasks();
90
91
// Promise has resolved
92
expect(result).toBe('async-value');
93
}));
94
95
it('should handle mixed async operations', fakeAsync(() => {
96
const results: string[] = [];
97
98
// Schedule various async operations
99
setTimeout(() => results.push('timeout-100'), 100);
100
setTimeout(() => results.push('timeout-200'), 200);
101
102
Promise.resolve().then(() => results.push('promise-1'));
103
Promise.resolve().then(() => results.push('promise-2'));
104
105
setInterval(() => results.push('interval'), 50);
106
107
// Flush microtasks first
108
flushMicrotasks();
109
expect(results).toEqual(['promise-1', 'promise-2']);
110
111
// Advance time and check results
112
tick(50);
113
expect(results).toContain('interval');
114
115
tick(50); // Total: 100ms
116
expect(results).toContain('timeout-100');
117
118
tick(100); // Total: 200ms
119
expect(results).toContain('timeout-200');
120
121
// Clean up periodic tasks
122
discardPeriodicTasks();
123
}));
124
125
it('should use proxy zone for test isolation', withProxyZone(() => {
126
let result = '';
127
128
// This test runs in an isolated proxy zone
129
setTimeout(() => {
130
result = 'completed';
131
}, 100);
132
133
tick(100);
134
expect(result).toBe('completed');
135
136
// ProxyZoneSpec automatically handles cleanup
137
}));
138
});
139
```
140
141
### FakeAsyncTestZoneSpec
142
143
Zone specification for fakeAsync testing environment.
144
145
```typescript { .api }
146
/**
147
* Zone specification for controlling time in tests
148
*/
149
class FakeAsyncTestZoneSpec implements ZoneSpec {
150
/** Zone name identifier */
151
name: 'fakeAsync';
152
153
/**
154
* Advance virtual time by specified milliseconds
155
* @param millis - Milliseconds to advance
156
* @param doTick - Optional callback for each tick
157
*/
158
tick(millis?: number, doTick?: (elapsed: number) => void): void;
159
160
/**
161
* Flush all pending tasks
162
* @param maxTurns - Maximum execution cycles
163
* @returns Number of tasks flushed
164
*/
165
flush(maxTurns?: number): number;
166
167
/**
168
* Flush only microtasks
169
*/
170
flushMicrotasks(): void;
171
172
/**
173
* Get current fake system time
174
* @returns Current virtual time in milliseconds
175
*/
176
getFakeSystemTime(): number;
177
}
178
```
179
180
**Usage Examples:**
181
182
```typescript
183
import 'zone.js/testing';
184
185
// Direct use of FakeAsyncTestZoneSpec
186
const fakeAsyncZone = Zone.current.fork(new FakeAsyncTestZoneSpec());
187
188
fakeAsyncZone.run(() => {
189
let value = 0;
190
191
setTimeout(() => value = 1, 100);
192
setTimeout(() => value = 2, 200);
193
194
// Control time directly on the zone spec
195
const zoneSpec = Zone.current.get('FakeAsyncTestZoneSpec');
196
197
zoneSpec.tick(100);
198
expect(value).toBe(1);
199
200
zoneSpec.tick(100); // Total: 200ms
201
expect(value).toBe(2);
202
});
203
```
204
205
### Async Testing
206
207
Testing utilities for asynchronous operations that need to complete naturally.
208
209
```typescript { .api }
210
/**
211
* Zone specification for async testing
212
*/
213
class AsyncTestZoneSpec implements ZoneSpec {
214
/** Zone name identifier */
215
name: 'async-test';
216
217
/**
218
* Wait for all pending asynchronous operations to complete
219
* @returns Promise that resolves when all async operations are done
220
*/
221
whenStable(): Promise<any>;
222
223
/**
224
* Check if there are any pending asynchronous operations
225
* @returns true if async operations are pending
226
*/
227
hasPendingAsyncTasks(): boolean;
228
229
/**
230
* Check if there are any pending microtasks
231
* @returns true if microtasks are pending
232
*/
233
hasPendingMicrotasks(): boolean;
234
235
/**
236
* Check if there are any pending macrotasks
237
* @returns true if macrotasks are pending
238
*/
239
hasPendingMacrotasks(): boolean;
240
}
241
```
242
243
**Usage Examples:**
244
245
```typescript
246
import 'zone.js/testing';
247
248
describe('Async Tests', () => {
249
let asyncZone: Zone;
250
let asyncZoneSpec: AsyncTestZoneSpec;
251
252
beforeEach(() => {
253
asyncZoneSpec = new AsyncTestZoneSpec();
254
asyncZone = Zone.current.fork(asyncZoneSpec);
255
});
256
257
it('should wait for async operations', async () => {
258
let completed = false;
259
260
asyncZone.run(() => {
261
setTimeout(() => {
262
completed = true;
263
}, 100);
264
265
fetch('/api/data').then(() => {
266
// Some async operation
267
});
268
});
269
270
// Wait for all async operations to complete
271
await asyncZoneSpec.whenStable();
272
273
expect(completed).toBe(true);
274
});
275
276
it('should check pending tasks', () => {
277
asyncZone.run(() => {
278
setTimeout(() => {}, 100);
279
280
expect(asyncZoneSpec.hasPendingMacrotasks()).toBe(true);
281
expect(asyncZoneSpec.hasPendingAsyncTasks()).toBe(true);
282
});
283
});
284
});
285
```
286
287
### Task Tracking
288
289
Zone specification for monitoring and tracking task execution.
290
291
```typescript { .api }
292
/**
293
* Zone specification for tracking task execution
294
*/
295
class TaskTrackingZoneSpec implements ZoneSpec {
296
/** Zone name identifier */
297
name: 'task-tracking';
298
299
/** Array of scheduled microtasks */
300
readonly microTasks: Task[];
301
302
/** Array of scheduled macrotasks */
303
readonly macroTasks: Task[];
304
305
/** Array of scheduled event tasks */
306
readonly eventTasks: Task[];
307
308
/**
309
* Clear all tracked tasks
310
*/
311
clear(): void;
312
313
/**
314
* Get tasks by type
315
* @param type - Task type to filter by
316
* @returns Array of tasks of the specified type
317
*/
318
getTasksByType(type: TaskType): Task[];
319
320
/**
321
* Get tasks by source
322
* @param source - Source identifier to filter by
323
* @returns Array of tasks from the specified source
324
*/
325
getTasksBySource(source: string): Task[];
326
}
327
```
328
329
**Usage Examples:**
330
331
```typescript
332
import 'zone.js/testing';
333
334
describe('Task Tracking Tests', () => {
335
let trackingZone: Zone;
336
let trackingSpec: TaskTrackingZoneSpec;
337
338
beforeEach(() => {
339
trackingSpec = new TaskTrackingZoneSpec();
340
trackingZone = Zone.current.fork(trackingSpec);
341
});
342
343
it('should track scheduled tasks', () => {
344
trackingZone.run(() => {
345
setTimeout(() => {}, 100);
346
setTimeout(() => {}, 200);
347
348
Promise.resolve().then(() => {});
349
350
document.addEventListener('click', () => {});
351
});
352
353
expect(trackingSpec.macroTasks).toHaveLength(2);
354
expect(trackingSpec.microTasks).toHaveLength(1);
355
expect(trackingSpec.eventTasks).toHaveLength(1);
356
357
// Check specific task sources
358
const timerTasks = trackingSpec.getTasksBySource('setTimeout');
359
expect(timerTasks).toHaveLength(2);
360
361
const promiseTasks = trackingSpec.getTasksByType('microTask');
362
expect(promiseTasks).toHaveLength(1);
363
});
364
365
it('should clear tracked tasks', () => {
366
trackingZone.run(() => {
367
setTimeout(() => {}, 100);
368
Promise.resolve().then(() => {});
369
});
370
371
expect(trackingSpec.macroTasks).toHaveLength(1);
372
expect(trackingSpec.microTasks).toHaveLength(1);
373
374
trackingSpec.clear();
375
376
expect(trackingSpec.macroTasks).toHaveLength(0);
377
expect(trackingSpec.microTasks).toHaveLength(0);
378
});
379
});
380
```
381
382
### Proxy Zone
383
384
Zone specification for test isolation and delegation control.
385
386
```typescript { .api }
387
/**
388
* Zone specification for test isolation and delegation
389
*/
390
class ProxyZoneSpec implements ZoneSpec {
391
/** Zone name identifier */
392
name: 'proxy';
393
394
/**
395
* Set the delegate zone specification
396
* @param delegateSpec - Zone spec to delegate operations to
397
*/
398
setDelegate(delegateSpec: ZoneSpec): void;
399
400
/**
401
* Get the current delegate zone specification
402
* @returns Current delegate zone spec
403
*/
404
getDelegate(): ZoneSpec;
405
406
/**
407
* Reset delegation to default behavior
408
*/
409
resetDelegate(): void;
410
411
/**
412
* Assert that no tasks are pending
413
* @param ignoreLeisure - Whether to ignore leisure tasks
414
*/
415
assertNoPendingTasks(ignoreLeisure?: boolean): void;
416
}
417
```
418
419
**Usage Examples:**
420
421
```typescript
422
import 'zone.js/testing';
423
424
describe('Proxy Zone Tests', () => {
425
let proxyZone: Zone;
426
let proxySpec: ProxyZoneSpec;
427
428
beforeEach(() => {
429
proxySpec = new ProxyZoneSpec();
430
proxyZone = Zone.current.fork(proxySpec);
431
});
432
433
it('should isolate tests', () => {
434
// Set up custom delegate for this test
435
const customDelegate = {
436
name: 'custom-test',
437
onHandleError: (delegate, current, target, error) => {
438
console.log('Custom error handling:', error);
439
return true; // Handle the error
440
}
441
};
442
443
proxySpec.setDelegate(customDelegate);
444
445
proxyZone.run(() => {
446
// Test code that might throw errors
447
throw new Error('Test error');
448
// Error will be handled by custom delegate
449
});
450
451
// Reset for next test
452
proxySpec.resetDelegate();
453
});
454
455
it('should assert no pending tasks', () => {
456
proxyZone.run(() => {
457
// Synchronous operations only
458
const result = 1 + 1;
459
expect(result).toBe(2);
460
});
461
462
// Should not throw - no pending tasks
463
proxySpec.assertNoPendingTasks();
464
});
465
466
it('should detect pending tasks', () => {
467
proxyZone.run(() => {
468
setTimeout(() => {}, 100);
469
});
470
471
// Should throw - there are pending tasks
472
expect(() => {
473
proxySpec.assertNoPendingTasks();
474
}).toThrow();
475
});
476
});
477
```
478
479
### Long Stack Trace
480
481
Zone specification for enhanced error stack traces across async boundaries.
482
483
```typescript { .api }
484
/**
485
* Enable long stack traces across async boundaries
486
* @param Zone - Zone constructor
487
*/
488
function patchLongStackTrace(Zone: ZoneType): void;
489
490
/**
491
* Zone specification for enhanced stack traces
492
*/
493
class LongStackTraceZoneSpec implements ZoneSpec {
494
/** Zone name identifier */
495
name: 'long-stack-trace';
496
497
/**
498
* Get enhanced stack trace for an error
499
* @param error - Error to get stack trace for
500
* @returns Enhanced stack trace string
501
*/
502
getLongStackTrace(error: Error): string;
503
}
504
```
505
506
**Usage Examples:**
507
508
```typescript
509
import 'zone.js/testing';
510
511
// Enable long stack traces
512
Zone.__load_patch('longStackTraceZone', (global, Zone) => {
513
patchLongStackTrace(Zone);
514
});
515
516
const longStackZone = Zone.current.fork(new LongStackTraceZoneSpec());
517
518
longStackZone.run(() => {
519
function asyncFunction() {
520
return new Promise((resolve, reject) => {
521
setTimeout(() => {
522
someOtherAsyncFunction()
523
.then(resolve)
524
.catch(reject);
525
}, 100);
526
});
527
}
528
529
function someOtherAsyncFunction() {
530
return Promise.reject(new Error('Async error'));
531
}
532
533
asyncFunction().catch(error => {
534
// Error will have enhanced stack trace showing
535
// the full async call chain
536
console.log(error.stack);
537
});
538
});
539
```