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

ModifierEffect
it.only / test.onlyRun only this test (in the file)
it.skip / test.skipSkip this test
it.todo / test.todoMark as pending / not yet written
it.fails / test.failsAssert the test is expected to fail
it.each / test.eachRun test with multiple data sets
describe.onlyRun only this suite
describe.skipSkip this suite
describe.todoMark suite as pending
describe.eachParameterize 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
})