Vitest Cheatsheet
Writing 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.
Basic Test Structure
import { describe, it, expect } from 'vitest' describe('Math utilities', () => { it('adds two numbers', () => { expect(1 + 1).toBe(2) }) it('subtracts two numbers', () => { expect(5 - 3).toBe(2) }) })
test is an alias for it — they are identical.
Nesting describe Blocks
describe('User service', () => { describe('create', () => { it('returns a new user', () => { /* ... */ }) it('throws on duplicate email', () => { /* ... */ }) }) describe('delete', () => { it('removes the user', () => { /* ... */ }) }) })
Test Modifiers
| Modifier | Effect |
|---|---|
it.only / test.only | Run only this test (in the file) |
it.skip / test.skip | Skip this test |
it.todo / test.todo | Mark as pending / not yet written |
it.fails / test.fails | Assert the test is expected to fail |
it.each / test.each | Run test with multiple data sets |
describe.only | Run only this suite |
describe.skip | Skip this suite |
describe.todo | Mark suite as pending |
describe.each | Parameterize a suite |
it.skip('not implemented yet', () => { /* ... */ }) it.todo('handle edge case') it.fails('known regression', () => { expect(brokenFn()).toBe('correct') // this must throw/fail })
Parameterized Tests with it.each
Array syntax:
it.each([ [1, 1, 2], [2, 3, 5], [0, -1, -1], ])('adds %i + %i = %i', (a, b, expected) => { expect(a + b).toBe(expected) })
Object syntax (recommended for readability):
it.each([ { a: 1, b: 1, expected: 2 }, { a: 2, b: 3, expected: 5 }, ])('adds $a + $b = $expected', ({ a, b, expected }) => { expect(a + b).toBe(expected) })
Template literal table:
it.each`
a | b | expected
${1} | ${1} | ${2}
${2} | ${3} | ${5}
`('adds $a + $b', ({ a, b, expected }) => {
expect(a + b).toBe(expected)
})describe.each
describe.each([ { role: 'admin', canDelete: true }, { role: 'viewer', canDelete: false }, ])('$role permissions', ({ role, canDelete }) => { it(`can${canDelete ? '' : 'not'} delete`, () => { expect(checkPermission(role, 'delete')).toBe(canDelete) }) })
Concurrent Tests
describe.concurrent('parallel suite', () => { it('test 1', async () => { /* runs in parallel */ }) it('test 2', async () => { /* runs in parallel */ }) }) // Or per-test: it.concurrent('parallel test', async () => { /* ... */ })
Use concurrent only when tests have no shared mutable state.
Conditional Skipping
const isCI = process.env.CI === 'true' it.skipIf(isCI)('skipped in CI', () => { /* ... */ }) it.runIf(!isCI)('runs only locally', () => { /* ... */ })
Test Context (Fixtures)
import { it } from 'vitest' it('has context', (ctx) => { // ctx.task — current task metadata // ctx.expect — isolated expect instance // ctx.skip() — skip from inside the test ctx.expect(1 + 1).toBe(2) })
Extending Context with Fixtures
import { test } from 'vitest' const myTest = test.extend<{ db: Database }>({ db: async ({}, use) => { const db = await Database.connect() await use(db) await db.close() }, }) myTest('reads from db', async ({ db }) => { const row = await db.find(1) expect(row).toBeDefined() })
Retry Flaky Tests
Options go in the second argument (the old third-argument options object was deprecated in Vitest 3 and removed in Vitest 4):
it('flaky network call', { retry: 3 }, async () => { const data = await fetchWithRetry() expect(data).toBeDefined() })
Or globally in config:
// vitest.config.ts export default defineConfig({ test: { retry: 2 } })
Timeout
it('slow operation', { timeout: 10_000 }, async () => { await longRunningTask() }) // A bare number as the last argument still works: it('also slow', async () => { await longRunningTask() }, 10_000) // Or globally: // test: { testTimeout: 10_000 }
Soft Assertions (non-failing mid-test)
import { expect } from 'vitest' it('checks multiple things', () => { expect.soft(1 + 1).toBe(2) expect.soft('hello').toBe('world') // records failure but continues expect.soft(true).toBe(true) // all soft failures reported at end })