or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions.mdbenchmarking.mdbrowser-testing.mdconfiguration.mdindex.mdmocking.mdnode-apis.mdreporters.mdtest-definition.mdtimers.mdtype-testing.md

timers.mddocs/

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