Vitest Cheatsheet
Async Tests
Use this Vitest reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Async/Await
import { it, expect } from 'vitest' it('fetches data', async () => { const data = await fetchData() expect(data).toEqual({ id: 1 }) })
Returning Promises
it('returns a promise', () => { return fetchData().then((data) => { expect(data.id).toBe(1) }) })
Vitest waits for any returned promise to resolve or reject.
Testing Async Errors
// Option 1: async/await + try/catch it('throws async error', async () => { await expect(fetchData(999)).rejects.toThrow('Not found') }) // Option 2: .rejects.toThrow it('rejects with an error', async () => { await expect(fetchData(-1)).rejects.toBeInstanceOf(TypeError) }) // Option 3: rejects.toMatchObject it('rejects with shape', async () => { await expect(asyncFn()).rejects.toMatchObject({ code: 404 }) })
Always await the .rejects chain — forgetting causes the test to pass even when it should fail.
Resolved Assertions
it('resolves with value', async () => { await expect(Promise.resolve(42)).resolves.toBe(42) await expect(fetchUser(1)).resolves.toMatchObject({ name: 'Alice' }) })
Callback APIs (no done callback)
Vitest does not support Jest's done callback — the first argument of a test function is the test context, so it('...', (done) => { done() }) throws (done is not a function). Wrap callback APIs in a Promise instead:
it('calls the callback', async () => { const result = await new Promise((resolve, reject) => { fetchWithCallback((err, value) => (err ? reject(err) : resolve(value))) }) expect(result).toBe('ok') })
Node-style (err, value) callbacks can also be converted with util.promisify:
import { promisify } from 'node:util' it('promisified callback', async () => { const fetchAsync = promisify(fetchWithCallback) await expect(fetchAsync()).resolves.toBe('ok') })
Add expect.assertions(n) when the assertion runs inside a callback, so the test fails if the callback never fires.
Async Timeouts
it('slow operation', async () => { const result = await longTask() expect(result).toBeDefined() }, 15_000) // 15-second timeout // Or globally: // test: { testTimeout: 15_000 }
Async Hooks
beforeAll(async () => { await db.connect() }) afterAll(async () => { await db.disconnect() }) beforeEach(async () => { await db.seed() })
waitFor — Polling Until Condition
import { waitFor } from '@testing-library/react' // RTL variant // or use vi.waitFor from Vitest 1.x+: import { vi } from 'vitest' it('waits for condition', async () => { startProcess() await vi.waitFor(() => { expect(getStatus()).toBe('done') }, { timeout: 3000, interval: 100 }) })
vi.waitFor retries the callback until it stops throwing or the timeout is reached.
vi.waitUntil
it('waits for truthy value', async () => { const result = await vi.waitUntil( () => getAsyncValue(), // retries until not null/undefined/false { timeout: 2000, interval: 50 } ) expect(result).toBe('ready') })
Fake Timers with Async
it('resolves after delay', async () => { vi.useFakeTimers() const promise = delay(5000).then(() => 'done') vi.advanceTimersByTimeAsync(5000) // async-safe version const result = await promise expect(result).toBe('done') vi.useRealTimers() })
Use advanceTimersByTimeAsync (not advanceTimersByTime) when promises depend on microtask resolution.
Concurrent Async Tests
it.concurrent('test 1', async () => { const data = await fetchA() expect(data).toBeDefined() }) it.concurrent('test 2', async () => { const data = await fetchB() expect(data).toBeDefined() })
Or for the whole suite:
describe.concurrent('parallel async', () => { it('a', async () => { /* ... */ }) it('b', async () => { /* ... */ }) })
Checking Assertions Count for Async
it('assert count enforces async path', async () => { expect.assertions(2) await asyncFn().then((result) => { expect(result.a).toBeDefined() expect(result.b).toBeDefined() }) })
Common Gotchas
- Missing await on
.rejects/.resolves— test silently passes without checking. expect.assertions(n)with callbacks — prevents false positives when callbacks are never called.- Fake timer + Promise ordering — always use
advanceTimersByTimeAsyncwhen promises are in the chain. - Unhandled rejections — Vitest treats these as test failures in Node 15+.
- async
beforeAllnot awaited — Vitest awaits hooks automatically when they return a promise.