0
# Timer Control
1
2
Vitest provides fake timer APIs for controlling time in tests, enabling deterministic testing of time-dependent code such as timeouts, intervals, and animations.
3
4
## Capabilities
5
6
### Fake Timers
7
8
Enable and configure fake timers to control time manually.
9
10
```typescript { .api }
11
interface VitestUtils {
12
/**
13
* Enable fake timers
14
* @param config - Optional timer configuration
15
* @returns VitestUtils for chaining
16
*/
17
useFakeTimers(config?: FakeTimerInstallOpts): VitestUtils;
18
19
/**
20
* Disable fake timers and restore real timers
21
* @returns VitestUtils for chaining
22
*/
23
useRealTimers(): VitestUtils;
24
25
/**
26
* Check if fake timers are currently enabled
27
* @returns true if fake timers are active
28
*/
29
isFakeTimers(): boolean;
30
}
31
32
interface FakeTimerInstallOpts {
33
/**
34
* Current system time (default: Date.now())
35
*/
36
now?: number | Date;
37
38
/**
39
* Maximum number of timers to run (prevents infinite loops)
40
* Default: 100_000
41
*/
42
loopLimit?: number;
43
44
/**
45
* Whether to mock Date.now(), performance.now(), etc.
46
* Default: true
47
*/
48
shouldAdvanceTime?: boolean;
49
50
/**
51
* Which timer APIs to mock
52
* Default: ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'setImmediate', 'clearImmediate', 'Date']
53
*/
54
toFake?: Array<
55
| 'setTimeout'
56
| 'clearTimeout'
57
| 'setInterval'
58
| 'clearInterval'
59
| 'setImmediate'
60
| 'clearImmediate'
61
| 'Date'
62
| 'performance'
63
| 'requestAnimationFrame'
64
| 'cancelAnimationFrame'
65
| 'requestIdleCallback'
66
| 'cancelIdleCallback'
67
>;
68
}
69
```
70
71
**Usage:**
72
73
```typescript
74
import { test, expect, vi, beforeEach, afterEach } from 'vitest';
75
76
beforeEach(() => {
77
vi.useFakeTimers();
78
});
79
80
afterEach(() => {
81
vi.useRealTimers();
82
});
83
84
test('fake timers', () => {
85
const callback = vi.fn();
86
87
setTimeout(callback, 1000);
88
89
// Time hasn't advanced yet
90
expect(callback).not.toHaveBeenCalled();
91
92
vi.advanceTimersByTime(1000);
93
94
// Now callback has been called
95
expect(callback).toHaveBeenCalled();
96
});
97
98
test('fake timers with config', () => {
99
vi.useFakeTimers({
100
now: new Date('2024-01-01T00:00:00Z'),
101
toFake: ['setTimeout', 'Date']
102
});
103
104
expect(Date.now()).toBe(new Date('2024-01-01T00:00:00Z').getTime());
105
106
vi.useRealTimers();
107
});
108
```
109
110
### Running Timers
111
112
Execute pending timers synchronously or asynchronously.
113
114
```typescript { .api }
115
interface VitestUtils {
116
/**
117
* Run only currently pending timers (not new ones)
118
* @returns VitestUtils for chaining
119
*/
120
runOnlyPendingTimers(): VitestUtils;
121
122
/**
123
* Async version of runOnlyPendingTimers
124
* @returns Promise resolving to VitestUtils
125
*/
126
runOnlyPendingTimersAsync(): Promise<VitestUtils>;
127
128
/**
129
* Run all pending timers (including new ones created by handlers)
130
* @returns VitestUtils for chaining
131
*/
132
runAllTimers(): VitestUtils;
133
134
/**
135
* Async version of runAllTimers
136
* @returns Promise resolving to VitestUtils
137
*/
138
runAllTimersAsync(): Promise<VitestUtils>;
139
140
/**
141
* Run all pending microtasks (process.nextTick, Promise callbacks)
142
* @returns VitestUtils for chaining
143
*/
144
runAllTicks(): VitestUtils;
145
}
146
```
147
148
**Usage:**
149
150
```typescript
151
import { test, expect, vi } from 'vitest';
152
153
test('run only pending timers', () => {
154
vi.useFakeTimers();
155
156
const callback1 = vi.fn();
157
const callback2 = vi.fn(() => {
158
// This creates a new timer during execution
159
setTimeout(callback3, 100);
160
});
161
const callback3 = vi.fn();
162
163
setTimeout(callback1, 100);
164
setTimeout(callback2, 100);
165
166
vi.runOnlyPendingTimers(); // Runs callback1 and callback2
167
168
expect(callback1).toHaveBeenCalled();
169
expect(callback2).toHaveBeenCalled();
170
expect(callback3).not.toHaveBeenCalled(); // Not run (created during execution)
171
172
vi.runOnlyPendingTimers(); // Now runs callback3
173
174
expect(callback3).toHaveBeenCalled();
175
176
vi.useRealTimers();
177
});
178
179
test('run all timers', () => {
180
vi.useFakeTimers();
181
182
const callback = vi.fn(() => {
183
if (callback.mock.calls.length < 3) {
184
setTimeout(callback, 100); // Recursive timer
185
}
186
});
187
188
setTimeout(callback, 100);
189
190
vi.runAllTimers(); // Runs all timers including recursive ones
191
192
expect(callback).toHaveBeenCalledTimes(3);
193
194
vi.useRealTimers();
195
});
196
197
test('run all ticks', async () => {
198
vi.useFakeTimers();
199
200
const callback = vi.fn();
201
202
process.nextTick(callback);
203
Promise.resolve().then(callback);
204
205
vi.runAllTicks(); // Run all microtasks
206
207
expect(callback).toHaveBeenCalledTimes(2);
208
209
vi.useRealTimers();
210
});
211
212
test('async timer execution', async () => {
213
vi.useFakeTimers();
214
215
const callback = vi.fn();
216
217
setTimeout(async () => {
218
await Promise.resolve();
219
callback();
220
}, 100);
221
222
await vi.runAllTimersAsync(); // Wait for async operations
223
224
expect(callback).toHaveBeenCalled();
225
226
vi.useRealTimers();
227
});
228
```
229
230
### Advancing Time
231
232
Manually advance time by specific amounts or to specific points.
233
234
```typescript { .api }
235
interface VitestUtils {
236
/**
237
* Advance timers by specified milliseconds
238
* @param ms - Milliseconds to advance
239
* @returns VitestUtils for chaining
240
*/
241
advanceTimersByTime(ms: number): VitestUtils;
242
243
/**
244
* Async version of advanceTimersByTime
245
* @param ms - Milliseconds to advance
246
* @returns Promise resolving to VitestUtils
247
*/
248
advanceTimersByTimeAsync(ms: number): Promise<VitestUtils>;
249
250
/**
251
* Advance to the next timer
252
* @returns VitestUtils for chaining
253
*/
254
advanceTimersToNextTimer(): VitestUtils;
255
256
/**
257
* Async version of advanceTimersToNextTimer
258
* @returns Promise resolving to VitestUtils
259
*/
260
advanceTimersToNextTimerAsync(): Promise<VitestUtils>;
261
262
/**
263
* Advance to the next animation frame (requestAnimationFrame)
264
* @returns VitestUtils for chaining
265
*/
266
advanceTimersToNextFrame(): VitestUtils;
267
}
268
```
269
270
**Usage:**
271
272
```typescript
273
import { test, expect, vi } from 'vitest';
274
275
test('advance by time', () => {
276
vi.useFakeTimers();
277
278
const callback = vi.fn();
279
280
setTimeout(callback, 1000);
281
282
vi.advanceTimersByTime(500);
283
expect(callback).not.toHaveBeenCalled();
284
285
vi.advanceTimersByTime(500);
286
expect(callback).toHaveBeenCalled();
287
288
vi.useRealTimers();
289
});
290
291
test('advance to next timer', () => {
292
vi.useFakeTimers();
293
294
const callback1 = vi.fn();
295
const callback2 = vi.fn();
296
const callback3 = vi.fn();
297
298
setTimeout(callback1, 100);
299
setTimeout(callback2, 200);
300
setTimeout(callback3, 300);
301
302
vi.advanceTimersToNextTimer(); // Advances to 100ms
303
expect(callback1).toHaveBeenCalled();
304
expect(callback2).not.toHaveBeenCalled();
305
306
vi.advanceTimersToNextTimer(); // Advances to 200ms
307
expect(callback2).toHaveBeenCalled();
308
expect(callback3).not.toHaveBeenCalled();
309
310
vi.advanceTimersToNextTimer(); // Advances to 300ms
311
expect(callback3).toHaveBeenCalled();
312
313
vi.useRealTimers();
314
});
315
316
test('advance to next frame', () => {
317
vi.useFakeTimers();
318
319
const callback = vi.fn();
320
321
requestAnimationFrame(callback);
322
323
expect(callback).not.toHaveBeenCalled();
324
325
vi.advanceTimersToNextFrame(); // Advance one frame (typically ~16ms)
326
327
expect(callback).toHaveBeenCalled();
328
329
vi.useRealTimers();
330
});
331
332
test('async advance', async () => {
333
vi.useFakeTimers();
334
335
const callback = vi.fn();
336
337
setTimeout(async () => {
338
await Promise.resolve();
339
callback();
340
}, 1000);
341
342
await vi.advanceTimersByTimeAsync(1000);
343
344
expect(callback).toHaveBeenCalled();
345
346
vi.useRealTimers();
347
});
348
```
349
350
### System Time
351
352
Control the system time (Date.now(), new Date(), etc.).
353
354
```typescript { .api }
355
interface VitestUtils {
356
/**
357
* Set the current system time
358
* @param time - Time to set (number, Date, or date string)
359
* @returns VitestUtils for chaining
360
*/
361
setSystemTime(time: number | string | Date): VitestUtils;
362
363
/**
364
* Get the current mocked system time
365
* @returns Mocked time as Date, or null if not using fake timers
366
*/
367
getMockedSystemTime(): Date | null;
368
369
/**
370
* Get the real system time (even when using fake timers)
371
* @returns Real current time in milliseconds
372
*/
373
getRealSystemTime(): number;
374
}
375
```
376
377
**Usage:**
378
379
```typescript
380
import { test, expect, vi } from 'vitest';
381
382
test('set system time', () => {
383
vi.useFakeTimers();
384
385
const mockDate = new Date('2024-01-01T00:00:00Z');
386
vi.setSystemTime(mockDate);
387
388
expect(Date.now()).toBe(mockDate.getTime());
389
expect(new Date().toISOString()).toBe('2024-01-01T00:00:00.000Z');
390
391
// Advance time
392
vi.advanceTimersByTime(1000);
393
394
expect(Date.now()).toBe(mockDate.getTime() + 1000);
395
396
vi.useRealTimers();
397
});
398
399
test('get mocked time', () => {
400
expect(vi.getMockedSystemTime()).toBeNull(); // Not using fake timers
401
402
vi.useFakeTimers();
403
vi.setSystemTime(new Date('2024-01-01'));
404
405
const mockedTime = vi.getMockedSystemTime();
406
expect(mockedTime?.toISOString()).toBe('2024-01-01T00:00:00.000Z');
407
408
vi.useRealTimers();
409
});
410
411
test('get real time', () => {
412
vi.useFakeTimers();
413
vi.setSystemTime(new Date('2024-01-01'));
414
415
const realTime = vi.getRealSystemTime();
416
expect(realTime).toBeGreaterThan(new Date('2024-01-01').getTime());
417
418
vi.useRealTimers();
419
});
420
```
421
422
### Timer Status
423
424
Query information about pending timers.
425
426
```typescript { .api }
427
interface VitestUtils {
428
/**
429
* Get the number of pending timers
430
* @returns Number of timers waiting to be executed
431
*/
432
getTimerCount(): number;
433
434
/**
435
* Clear all pending timers
436
* @returns VitestUtils for chaining
437
*/
438
clearAllTimers(): VitestUtils;
439
}
440
```
441
442
**Usage:**
443
444
```typescript
445
import { test, expect, vi } from 'vitest';
446
447
test('timer count', () => {
448
vi.useFakeTimers();
449
450
expect(vi.getTimerCount()).toBe(0);
451
452
setTimeout(() => {}, 100);
453
setTimeout(() => {}, 200);
454
setInterval(() => {}, 300);
455
456
expect(vi.getTimerCount()).toBe(3);
457
458
vi.advanceTimersByTime(100);
459
460
expect(vi.getTimerCount()).toBe(2); // One timeout executed
461
462
vi.clearAllTimers();
463
464
expect(vi.getTimerCount()).toBe(0);
465
466
vi.useRealTimers();
467
});
468
```
469
470
## Common Patterns
471
472
### Testing Debounce
473
474
```typescript
475
import { test, expect, vi } from 'vitest';
476
477
function debounce<T extends (...args: any[]) => any>(
478
fn: T,
479
delay: number
480
): (...args: Parameters<T>) => void {
481
let timeoutId: NodeJS.Timeout;
482
return (...args: Parameters<T>) => {
483
clearTimeout(timeoutId);
484
timeoutId = setTimeout(() => fn(...args), delay);
485
};
486
}
487
488
test('debounce', () => {
489
vi.useFakeTimers();
490
491
const callback = vi.fn();
492
const debounced = debounce(callback, 300);
493
494
debounced('a');
495
debounced('b');
496
debounced('c');
497
498
// Callback not called yet
499
expect(callback).not.toHaveBeenCalled();
500
501
vi.advanceTimersByTime(299);
502
expect(callback).not.toHaveBeenCalled();
503
504
vi.advanceTimersByTime(1);
505
expect(callback).toHaveBeenCalledWith('c');
506
expect(callback).toHaveBeenCalledTimes(1);
507
508
vi.useRealTimers();
509
});
510
```
511
512
### Testing Throttle
513
514
```typescript
515
import { test, expect, vi } from 'vitest';
516
517
function throttle<T extends (...args: any[]) => any>(
518
fn: T,
519
delay: number
520
): (...args: Parameters<T>) => void {
521
let lastCall = 0;
522
return (...args: Parameters<T>) => {
523
const now = Date.now();
524
if (now - lastCall >= delay) {
525
lastCall = now;
526
fn(...args);
527
}
528
};
529
}
530
531
test('throttle', () => {
532
vi.useFakeTimers();
533
534
const callback = vi.fn();
535
const throttled = throttle(callback, 100);
536
537
throttled('a');
538
throttled('b');
539
throttled('c');
540
541
// Only first call executes
542
expect(callback).toHaveBeenCalledTimes(1);
543
expect(callback).toHaveBeenCalledWith('a');
544
545
vi.advanceTimersByTime(100);
546
547
throttled('d');
548
549
expect(callback).toHaveBeenCalledTimes(2);
550
expect(callback).toHaveBeenCalledWith('d');
551
552
vi.useRealTimers();
553
});
554
```
555
556
### Testing Intervals
557
558
```typescript
559
import { test, expect, vi } from 'vitest';
560
561
test('interval', () => {
562
vi.useFakeTimers();
563
564
const callback = vi.fn();
565
566
const intervalId = setInterval(callback, 100);
567
568
// No calls yet
569
expect(callback).not.toHaveBeenCalled();
570
571
vi.advanceTimersByTime(100);
572
expect(callback).toHaveBeenCalledTimes(1);
573
574
vi.advanceTimersByTime(100);
575
expect(callback).toHaveBeenCalledTimes(2);
576
577
vi.advanceTimersByTime(100);
578
expect(callback).toHaveBeenCalledTimes(3);
579
580
clearInterval(intervalId);
581
582
vi.advanceTimersByTime(100);
583
expect(callback).toHaveBeenCalledTimes(3); // No new calls
584
585
vi.useRealTimers();
586
});
587
```
588
589
### Testing Animations
590
591
```typescript
592
import { test, expect, vi } from 'vitest';
593
594
test('animation', () => {
595
vi.useFakeTimers();
596
597
const callback = vi.fn();
598
599
requestAnimationFrame(callback);
600
requestAnimationFrame(callback);
601
requestAnimationFrame(callback);
602
603
expect(callback).not.toHaveBeenCalled();
604
605
// Advance one frame
606
vi.advanceTimersToNextFrame();
607
608
expect(callback).toHaveBeenCalledTimes(3);
609
610
vi.useRealTimers();
611
});
612
```
613
614
### Testing Retries with Backoff
615
616
```typescript
617
import { test, expect, vi } from 'vitest';
618
619
async function retryWithBackoff(
620
fn: () => Promise<any>,
621
maxRetries: number = 3,
622
initialDelay: number = 100
623
): Promise<any> {
624
let lastError;
625
626
for (let i = 0; i < maxRetries; i++) {
627
try {
628
return await fn();
629
} catch (error) {
630
lastError = error;
631
if (i < maxRetries - 1) {
632
await new Promise(resolve =>
633
setTimeout(resolve, initialDelay * Math.pow(2, i))
634
);
635
}
636
}
637
}
638
639
throw lastError;
640
}
641
642
test('retry with backoff', async () => {
643
vi.useFakeTimers();
644
645
let attempts = 0;
646
const mockFn = vi.fn(async () => {
647
attempts++;
648
if (attempts < 3) {
649
throw new Error('Failed');
650
}
651
return 'Success';
652
});
653
654
const promise = retryWithBackoff(mockFn);
655
656
// First attempt fails immediately
657
await vi.runAllTicksAsync();
658
expect(mockFn).toHaveBeenCalledTimes(1);
659
660
// Wait 100ms for second attempt
661
await vi.advanceTimersByTimeAsync(100);
662
expect(mockFn).toHaveBeenCalledTimes(2);
663
664
// Wait 200ms for third attempt
665
await vi.advanceTimersByTimeAsync(200);
666
expect(mockFn).toHaveBeenCalledTimes(3);
667
668
const result = await promise;
669
expect(result).toBe('Success');
670
671
vi.useRealTimers();
672
});
673
```
674
675
## Best Practices
676
677
1. **Always restore real timers**: Use `afterEach(() => vi.useRealTimers())` to avoid affecting other tests
678
679
2. **Use async versions for async code**: When testing code with async operations, use `runAllTimersAsync()` or `advanceTimersByTimeAsync()`
680
681
3. **Be careful with infinite loops**: Fake timers have a loop limit to prevent infinite loops. Increase with `loopLimit` option if needed
682
683
4. **Clear timers in cleanup**: Use `vi.clearAllTimers()` in `afterEach` to prevent timers from affecting other tests
684
685
5. **Mock specific timer APIs**: Use `toFake` option to mock only the timer APIs you need
686
687
6. **Test deterministically**: Fake timers make time-dependent code deterministic and fast
688