
Vitest
- 2.5k installs
- 696 repo stars
- Updated July 27, 2026
- onmax/nuxt-skills
vitest is a Nuxt skills entry for writing Vitest tests in Vite projects with mocks, coverage, and config guidance.
About
The vitest skill covers the Vite-native Vitest framework with a Jest-compatible API for unit and integration tests in Vite, Vue, React, and Svelte projects. Quick start installs vitest as a dev dependency and defines vitest.config.ts with globals and node or jsdom environments. Tests use describe, it, expect, and vi imports for mocks. Reference files split configuration and CLI, test API and hooks, vi.fn and vi.mock mocking, expect snapshots and coverage utilities, and advanced environments plus browser mode guidance. The skill instructs agents to load only relevant reference files per task rather than reading all docs at once. Cross-skill pointers defer Vue component testing to the vue skill, library patterns to ts-library, and shared Vite settings to the vite skill. Use cases include mocking modules and timers, running concurrent tests, and TypeScript type testing. License is MIT and installation assumes an existing Vite-based project toolchain.
- Vitest is Vite-native with Jest-compatible describe, it, and expect APIs.
- Configure environment node or jsdom in vitest.config.ts.
- Reference files cover config, mocking, utilities, and advanced modes.
- Load only relevant reference markdown per task, not all at once.
- Cross-links to vue, ts-library, and vite skills for related patterns.
Vitest by the numbers
- 2,502 all-time installs (skills.sh)
- +60 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #342 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
vitest capabilities & compatibility
- Capabilities
- vitest.config.ts bootstrap with environments · describe/it test structure and hooks references · vi.fn, vi.mock, and timer mocking patterns · coverage, snapshots, and filtering utilities · progressive reference loading guidance
- Use cases
- testing · frontend
What vitest says it does
Vite-native testing framework with Jest-compatible API.
DO NOT load all files at once.
npx skills add https://github.com/onmax/nuxt-skills --skill vitestAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2.5k |
|---|---|
| repo stars | ★ 696 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 27, 2026 |
| Repository | onmax/nuxt-skills ↗ |
How do I set up Vitest and write tests with mocks and coverage in a Vite project?
Write Vitest unit and integration tests for Vite projects with config, mocks, coverage, and parallel execution.
Who is it for?
Vite, Vue, React, or Svelte projects adopting Vitest for unit and integration tests.
Skip if: Skip for Jest-only Create React App projects not using Vite without migration.
When should I use this skill?
User asks about vitest.config.ts, vi.mock, describe/it tests, or Vite test coverage.
What you get
Working vitest.config.ts, test files, and targeted reference guidance for mocks or coverage needs.
- vitest.config.ts
- Multi-project test setup
- Per-file environment directives
By the numbers
- Documents four Vitest test environments: node, jsdom, happy-dom, edge-runtime
- Includes install commands for two DOM packages: jsdom and happy-dom
Files
Vitest
Vite-native testing framework with Jest-compatible API.
When to Use
- Writing unit/integration tests for Vite projects
- Testing Vue/React/Svelte components
- Mocking modules, timers, or dates
- Running concurrent/parallel tests
- Type testing with TypeScript
Quick Start
npm i -D vitest// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true,
environment: 'node', // or 'jsdom' for DOM tests
},
})// example.test.ts
import { describe, expect, it, vi } from 'vitest'
describe('math', () => {
it('adds numbers', () => {
expect(1 + 1).toBe(2)
})
})Reference Files
| Task | File |
|---|---|
| Configuration, CLI, projects | config.md |
| test/describe, hooks, fixtures | test-api.md |
| vi.fn, vi.mock, timers, spies | mocking.md |
| expect, snapshots, coverage, filtering | utilities.md |
| Environments, type testing, browser mode | advanced.md |
Loading Files
Consider loading these reference files based on your task:
- [ ] references/config.md - if setting up vitest.config.ts, CLI, or workspace projects
- [ ] references/test-api.md - if writing test/describe blocks, using hooks, or test fixtures
- [ ] references/mocking.md - if mocking modules, timers, dates, or using spies
- [ ] references/utilities.md - if writing assertions, snapshots, or configuring coverage
- [ ] references/advanced.md - if configuring test environments, type testing, or browser mode
DO NOT load all files at once. Load only what's relevant to your current task.
Cross-Skill References
- Vue component testing → Use
vueskill for component patterns - Library testing → Use
ts-libraryskill for library patterns - Vite configuration → Use
viteskill for shared config
Advanced
Test Environments
Available: node (default), jsdom, happy-dom, edge-runtime
defineConfig({
test: {
environment: 'jsdom',
environmentOptions: {
jsdom: { url: 'http://localhost' },
},
},
})Install packages:
npm i -D jsdom # Full browser simulation
npm i -D happy-dom # Faster, fewer APIsPer-file environment:
// @vitest-environment jsdom
test('DOM test', () => {
const div = document.createElement('div')
expect(div).toBeInstanceOf(HTMLDivElement)
})Multiple environments via projects:
defineConfig({
test: {
projects: [
{ test: { name: 'unit', include: ['tests/unit/**'], environment: 'node' } },
{ test: { name: 'dom', include: ['tests/dom/**'], environment: 'jsdom' } },
],
},
})Custom Environment
// vitest-environment-custom/index.ts
import type { Environment } from 'vitest/runtime'
export default <Environment>{
name: 'custom',
viteEnvironment: 'ssr',
setup() {
globalThis.myGlobal = 'value'
return {
teardown() { delete globalThis.myGlobal },
}
},
}Type Testing
Test TypeScript types with .test-d.ts files:
// math.test-d.ts
import { expectTypeOf } from 'vitest'
import { add } from './math'
test('add returns number', () => {
expectTypeOf(add).returns.toBeNumber()
})expectTypeOf API
// Basic types
expectTypeOf<string>().toBeString()
expectTypeOf<number>().toBeNumber()
expectTypeOf<boolean>().toBeBoolean()
expectTypeOf<null>().toBeNull()
expectTypeOf<undefined>().toBeUndefined()
expectTypeOf<never>().toBeNever()
expectTypeOf<any>().toBeAny()
expectTypeOf<unknown>().toBeUnknown()
expectTypeOf<[]>().toBeArray()
expectTypeOf<Function>().toBeFunction()
// Value types
const value = 'hello'
expectTypeOf(value).toBeString()
expectTypeOf(obj).toMatchTypeOf<{ name: string }>()
expectTypeOf(obj).toHaveProperty('name')
// Functions
expectTypeOf(greet).parameters.toEqualTypeOf<[string]>()
expectTypeOf(greet).returns.toBeString()
expectTypeOf(greet).parameter(0).toBeString()
// Equality
expectTypeOf<B>().toMatchTypeOf<A>() // Subset matching
expectTypeOf<A>().toEqualTypeOf<B>() // Exact match
expectTypeOf<A>().not.toEqualTypeOf<B>()
// Nullable
expectTypeOf<string | null>().toBeNullable()assertType
import { assertType } from 'vitest'
// @ts-expect-error - should fail type check
assertType<string>(result)
assertType<User | null>(result) // CorrectRun: vitest typecheck or vitest --typecheck
Projects (Workspaces)
defineConfig({
test: {
projects: [
'packages/*', // Glob for package configs
{
test: {
name: 'unit',
include: ['tests/unit/**/*.test.ts'],
environment: 'node',
},
},
],
},
})Providing Values
defineConfig({
test: {
projects: [
{
test: {
name: 'staging',
provide: { apiUrl: 'https://staging.api.com' },
},
},
],
},
})
// In tests
import { inject } from 'vitest'
const url = inject('apiUrl')Running Specific Projects
vitest --project unit
vitest --project unit --project e2e
vitest --project.ignore browserBrowser Mode
Real browser testing (separate from environments):
defineConfig({
test: {
browser: {
enabled: true,
name: 'chromium', // or 'firefox', 'webkit'
provider: 'playwright',
},
},
})CSS in Tests
defineConfig({
test: {
css: true,
// Or with options
css: {
include: /\.module\.css$/,
modules: { classNameStrategy: 'non-scoped' },
},
},
})External Dependencies
Fix deps that fail with CSS/asset errors:
defineConfig({
test: {
server: {
deps: {
inline: ['problematic-package'],
},
},
},
})Global Setup
defineConfig({
test: {
globalSetup: ['./tests/global-setup.ts'],
},
})
// tests/global-setup.ts
export default async function setup() {
// Run before all tests
return async () => {
// Teardown after all tests
}
}Benchmarking
import { bench, describe } from 'vitest'
describe('sort', () => {
bench('native', () => {
[1, 5, 4, 2, 3].sort((a, b) => a - b)
})
bench('lodash', () => {
_.sortBy([1, 5, 4, 2, 3])
})
})Run: vitest bench
Configuration & CLI
Config File Setup
Vitest reads from vitest.config.ts or vite.config.ts.
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
// test options
},
})With existing Vite config:
// vite.config.ts
/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
},
})Merge configs:
import { defineConfig, mergeConfig } from 'vitest/config'
import viteConfig from './vite.config'
export default mergeConfig(viteConfig, defineConfig({
test: { environment: 'jsdom' },
}))Common Options
defineConfig({
test: {
globals: true, // Enable global APIs without imports
environment: 'node', // 'node', 'jsdom', 'happy-dom'
setupFiles: ['./tests/setup.ts'], // Run before each test file
include: ['**/*.{test,spec}.{js,ts,jsx,tsx}'],
exclude: ['**/node_modules/**', '**/dist/**'],
testTimeout: 5000,
hookTimeout: 10000,
watch: true,
coverage: {
provider: 'v8', // or 'istanbul'
reporter: ['text', 'html'],
include: ['src/**/*.ts'],
},
isolate: true, // Each file in separate process
fileParallelism: true, // Run test files in parallel
pool: 'threads', // 'threads', 'forks', 'vmThreads'
poolOptions: {
threads: { maxThreads: 4, minThreads: 1 },
},
clearMocks: true,
restoreMocks: true,
retry: 0,
bail: 0,
},
})Conditional Config
export default defineConfig(({ mode }) => ({
plugins: mode === 'test' ? [] : [myPlugin()],
test: { /* options */ },
}))Projects (Monorepos)
defineConfig({
test: {
projects: [
'packages/*',
{
test: {
name: 'unit',
include: ['tests/unit/**/*.test.ts'],
environment: 'node',
},
},
{
test: {
name: 'integration',
include: ['tests/integration/**/*.test.ts'],
environment: 'jsdom',
},
},
],
},
})CLI Commands
vitest # Watch mode in dev, run mode in CI
vitest run # Single run without watch
vitest run --coverage # With coverage
vitest related src/index.ts --run # Tests importing specific files
vitest bench # Benchmark tests only
vitest list --json # List tests without running
vitest typecheck # Type tests onlyCommon CLI Options
--config <path> # Config file path
--project <name> # Run specific project
-t, --testNamePattern # Filter by test name
--changed # Only changed files
--changed HEAD~1 # Since last commit
--reporter <name> # default, verbose, dot, json, html
--coverage # Enable coverage
--shard <index>/<count> # Split across machines
--bail <n> # Stop after n failures
--retry <n> # Retry failed tests
--environment <env> # jsdom, happy-dom, node
--globals # Enable global APIs
--inspect # Node inspector
--silent # Suppress outputSharding for CI
# Split across 3 machines
vitest run --shard=1/3 --reporter=blob
vitest run --shard=2/3 --reporter=blob
vitest run --shard=3/3 --reporter=blob
# Merge reports
vitest --merge-reports --reporter=junitWatch Mode Shortcuts
a- Run all testsf- Run only failedu- Update snapshotsp- Filter by filenamet- Filter by test nameq- Quit
Package.json Scripts
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:ui": "vitest --ui",
"coverage": "vitest run --coverage"
}
}Mocking
Mock Functions
import { vi } from 'vitest'
const fn = vi.fn()
fn('hello')
expect(fn).toHaveBeenCalled()
expect(fn).toHaveBeenCalledWith('hello')
// With implementation
const add = vi.fn((a, b) => a + b)
expect(add(1, 2)).toBe(3)
// Mock return values
fn.mockReturnValue(42)
fn.mockReturnValueOnce(1).mockReturnValueOnce(2)
fn.mockResolvedValue({ data: true })
fn.mockRejectedValue(new Error('fail'))
fn.mockImplementation((x) => x * 2)
fn.mockImplementationOnce(() => 'first call')Spying
const cart = { getTotal: () => 100 }
const spy = vi.spyOn(cart, 'getTotal')
cart.getTotal()
expect(spy).toHaveBeenCalled()
spy.mockReturnValue(200)
spy.mockRestore() // Restore original
// Spy on getter/setter
vi.spyOn(obj, 'prop', 'get').mockReturnValue('value')Module Mocking
// vi.mock is hoisted to top of file
vi.mock('./api', () => ({
fetchUser: vi.fn(() => ({ id: 1, name: 'Mock' })),
}))
import { fetchUser } from './api'
test('mocked module', () => {
expect(fetchUser()).toEqual({ id: 1, name: 'Mock' })
})Partial Mock
vi.mock('./utils', async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
specificFunction: vi.fn(),
}
})Auto-mock with Spy
vi.mock('./calculator', { spy: true })
import { add } from './calculator'
test('spy on module', () => {
const result = add(1, 2) // Real implementation
expect(result).toBe(3)
expect(add).toHaveBeenCalledWith(1, 2)
})Manual Mocks (mocks)
src/
__mocks__/
axios.ts # Mocks 'axios'
api/
__mocks__/
client.ts # Mocks './client'
client.tsvi.mock('axios')
vi.mock('./api/client')Dynamic Mocking (vi.doMock)
Not hoisted - for dynamic imports:
test('dynamic mock', async () => {
vi.doMock('./config', () => ({ apiUrl: 'http://test.local' }))
const { apiUrl } = await import('./config')
expect(apiUrl).toBe('http://test.local')
vi.doUnmock('./config')
})
// Wait for all dynamic imports to load
await vi.dynamicImportSettled()Hoisted Variables
const mockFn = vi.hoisted(() => vi.fn())
vi.mock('./module', () => ({
getData: mockFn,
}))
test('hoisted mock', () => {
mockFn.mockReturnValue('test')
expect(getData()).toBe('test')
})Mock Timers
beforeEach(() => { vi.useFakeTimers() })
afterEach(() => { vi.useRealTimers() })
test('timers', () => {
const fn = vi.fn()
setTimeout(fn, 1000)
expect(fn).not.toHaveBeenCalled()
vi.advanceTimersByTime(1000)
expect(fn).toHaveBeenCalled()
})
// Other methods
vi.runAllTimers()
vi.runOnlyPendingTimers()
vi.advanceTimersToNextTimer()
vi.advanceTimersToNextFrame() // requestAnimationFrame
vi.clearAllTimers()
vi.getTimerCount()
// Async timer methods
await vi.advanceTimersByTimeAsync(100)
await vi.runAllTimersAsync()Mock Dates
vi.setSystemTime(new Date('2024-01-01'))
expect(new Date().getFullYear()).toBe(2024)
vi.useRealTimers() // Restore
vi.getMockedSystemTime() // Get mocked date
vi.getRealSystemTime() // Get real time (ms)Mock Globals & Environment
vi.stubGlobal('fetch', vi.fn(() =>
Promise.resolve({ json: () => ({ data: 'mock' }) })
))
vi.unstubAllGlobals()
vi.stubEnv('API_KEY', 'test-key')
expect(import.meta.env.API_KEY).toBe('test-key')
vi.unstubAllEnvs()Mock Object
const original = { method: () => 'real', nested: { fn: () => 'nested' } }
const mocked = vi.mockObject(original)
mocked.method.mockReturnValue('mocked')
const spied = vi.mockObject(original, { spy: true })
spied.method() // 'real'
expect(spied.method).toHaveBeenCalled()Clearing Mocks
fn.mockClear() // Clear call history
fn.mockReset() // Clear history + implementation
fn.mockRestore() // Restore original (for spies)
vi.clearAllMocks()
vi.resetAllMocks()
vi.restoreAllMocks()Config Auto-Reset
defineConfig({
test: {
clearMocks: true,
mockReset: true,
restoreMocks: true,
unstubEnvs: true,
unstubGlobals: true,
},
})Waiting Utilities
await vi.waitFor(async () => {
const el = document.querySelector('.loaded')
expect(el).toBeTruthy()
}, { timeout: 5000, interval: 100 })
const element = await vi.waitUntil(
() => document.querySelector('.loaded'),
{ timeout: 5000 }
)TypeScript Helper
import { myFn } from './module'
vi.mock('./module')
vi.mocked(myFn).mockReturnValue('typed')
vi.mocked(myModule, { deep: true })
vi.mocked(fn, { partial: true }).mockResolvedValue({ ok: true })Test API
Basic Tests
import { describe, expect, it, test } from 'vitest'
test('adds numbers', () => {
expect(1 + 1).toBe(2)
})
// Alias: it
it('works the same', () => {
expect(true).toBe(true)
})Async Tests
test('async test', async () => {
const result = await fetchData()
expect(result).toBeDefined()
})Test Options
test('with options', { timeout: 10_000, retry: 2 }, async () => {})
test('with tags', { tags: ['db', 'slow'] }, async () => {})Modifiers
test.skip('skipped', () => {})
test.only('only this runs', () => {})
test.todo('implement later')
test.fails('expected to fail', () => { expect(1).toBe(2) })
test.skipIf(process.env.CI)('not in CI', () => {})
test.runIf(process.env.CI)('only in CI', () => {})
// Dynamic skip
test('dynamic', ({ skip }) => {
skip(someCondition, 'reason')
})Concurrent & Sequential
test.concurrent('parallel 1', async ({ expect }) => {})
test.concurrent('parallel 2', async ({ expect }) => {})
test.sequential('must run alone', async () => {})Important: Use { expect } from context for concurrent tests.
Parameterized Tests
test.each([
[1, 1, 2],
[1, 2, 3],
])('add(%i, %i) = %i', (a, b, expected) => {
expect(a + b).toBe(expected)
})
test.each([
{ a: 1, b: 1, expected: 2 },
{ a: 1, b: 2, expected: 3 },
])('add($a, $b) = $expected', ({ a, b, expected }) => {
expect(a + b).toBe(expected)
})
// test.for - preferred, doesn't spread arrays
test.for([
[1, 1, 2],
[1, 2, 3],
])('add(%i, %i) = %i', ([a, b, expected], { expect }) => {
expect(a + b).toBe(expected)
})Describe/Suite
describe('Math', () => {
test('adds', () => expect(1 + 1).toBe(2))
test('subtracts', () => expect(3 - 1).toBe(2))
})
// Nested
describe('User', () => {
describe('when logged in', () => {
test('shows dashboard', () => {})
})
})
// Modifiers
describe.skip('skipped', () => {})
describe.only('only this', () => {})
describe.concurrent('parallel', () => {})
describe.shuffle('random order', () => {}) // Randomize test order
describe.each([{ name: 'Chrome' }, { name: 'Firefox' }])('$name', ({ name }) => {})Lifecycle Hooks
import { afterAll, afterEach, beforeAll, beforeEach } from 'vitest'
beforeAll(async () => { await setupDatabase() })
afterAll(async () => { await teardownDatabase() })
beforeEach(async () => { await clearTestData() })
afterEach(async () => { await cleanupMocks() })
// Return cleanup function
beforeAll(async () => {
const server = await startServer()
return async () => { await server.close() }
})Around Hooks
Wrap test execution with setup/teardown logic:
import { aroundEach, aroundAll } from 'vitest'
aroundEach(async (runTest) => {
await db.beginTransaction()
await runTest() // Must be called!
await db.rollback()
})
aroundAll(async (runAll) => {
const server = await startServer()
await runAll()
await server.close()
})Test Hooks
import { onTestFailed, onTestFinished } from 'vitest'
test('with cleanup', () => {
const db = connect()
onTestFinished(() => db.close())
onTestFailed(({ task }) => { console.log('Failed:', task.result?.errors) })
})
// Reusable pattern
function useTestDb() {
const db = connect()
onTestFinished(() => db.close())
return db
}Custom Fixtures
import { test as base } from 'vitest'
const test = base.extend<{ db: Database; user: User }>({
db: async ({}, use) => {
const db = await createDb()
await use(db)
await db.close()
},
user: async ({ db }, use) => {
const user = await db.createUser({ name: 'Test' })
await use(user)
await db.deleteUser(user.id)
},
})
test('query', async ({ db, user }) => {
const found = await db.findUser(user.id)
expect(found).toEqual(user)
})
// Fixture options
const test = base.extend({
setup: [async ({}, use) => { await use() }, { auto: true }], // Always run
connection: [async ({}, use) => { /* ... */ }, { scope: 'file' }], // Once per file
})Hook Execution Order
1. beforeAll (in order) 2. beforeEach (in order) 3. Test 4. afterEach (reverse order) 5. afterAll (reverse order)
Configure with sequence.hooks: 'stack' | 'list' | 'parallel'
Utilities
Expect API
import { expect } from 'vitest'
// Equality
expect(1 + 1).toBe(2) // Strict ===
expect({ a: 1 }).toEqual({ a: 1 }) // Deep equality
expect({ a: 1 }).toStrictEqual({ a: 1 }) // Checks undefined props
// Truthiness
expect(true).toBeTruthy()
expect(false).toBeFalsy()
expect(null).toBeNull()
expect(undefined).toBeUndefined()
expect('value').toBeDefined()
// Special
expect('a').toBeOneOf(['a', 'b', 'c'])
expect(value).toSatisfy((v) => v > 0)
// Numbers
expect(10).toBeGreaterThan(5)
expect(10).toBeGreaterThanOrEqual(10)
expect(5).toBeLessThan(10)
expect(0.1 + 0.2).toBeCloseTo(0.3, 5)
// Strings
expect('hello world').toMatch(/world/)
expect('hello').toContain('ell')
// Arrays
expect([1, 2, 3]).toContain(2)
expect([{ a: 1 }]).toContainEqual({ a: 1 })
expect([1, 2, 3]).toHaveLength(3)
// Objects
expect({ a: 1, b: 2 }).toHaveProperty('a')
expect({ a: 1, b: 2 }).toHaveProperty('a', 1)
expect({ a: { b: 1 } }).toHaveProperty('a.b', 1)
expect({ a: 1 }).toMatchObject({ a: 1 })
// Types
expect('string').toBeTypeOf('string')
expect(new Date()).toBeInstanceOf(Date)
// Negation
expect(1).not.toBe(2)Error Assertions
// Sync - wrap in function
expect(() => throwError()).toThrow()
expect(() => throwError()).toThrow('message')
expect(() => throwError()).toThrow(/pattern/)
expect(() => throwError()).toThrow(CustomError)
// Async - use rejects
await expect(asyncThrow()).rejects.toThrow('error')Promise Assertions
await expect(Promise.resolve(1)).resolves.toBe(1)
await expect(Promise.reject('error')).rejects.toBe('error')Spy/Mock Assertions
const fn = vi.fn()
fn('arg1', 'arg2')
fn('arg3')
expect(fn).toHaveBeenCalled()
expect(fn).toHaveBeenCalledTimes(2)
expect(fn).toHaveBeenCalledWith('arg1', 'arg2')
expect(fn).toHaveBeenLastCalledWith('arg3')
expect(fn).toHaveBeenNthCalledWith(1, 'arg1', 'arg2')
expect(fn).toHaveReturned()
expect(fn).toHaveReturnedWith(value)
// Call order
const fn1 = vi.fn()
const fn2 = vi.fn()
fn1()
fn2()
expect(fn1).toHaveBeenCalledBefore(fn2)
expect(fn2).toHaveBeenCalledAfter(fn1)Asymmetric Matchers
expect({ id: 1, name: 'test' }).toEqual({
id: expect.any(Number),
name: expect.any(String),
})
expect({ a: 1, b: 2, c: 3 }).toEqual(expect.objectContaining({ a: 1 }))
expect([1, 2, 3, 4]).toEqual(expect.arrayContaining([1, 3]))
expect('hello world').toEqual(expect.stringContaining('world'))
expect('hello world').toEqual(expect.stringMatching(/world$/))
expect({ value: null }).toEqual({ value: expect.anything() })
expect([1, 2]).toEqual(expect.not.arrayContaining([3]))
expect(0.1 + 0.2).toEqual(expect.closeTo(0.3, 5)) // Floating pointSoft & Poll Assertions
// Continue after failure
expect.soft(1).toBe(2) // Marks failed but continues
expect.soft(2).toBe(3) // Also runs
// Retry until passes
await expect.poll(() => fetchStatus()).toBe('ready')
await expect.poll(
() => document.querySelector('.element'),
{ interval: 100, timeout: 5000 }
).toBeTruthy()Assertion Count
test('async assertions', async () => {
expect.assertions(2) // Exactly 2 must run
expect.hasAssertions() // At least 1 must run
})Custom Matchers
expect.extend({
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling
return {
pass,
message: () => `expected ${received} to be within ${floor} - ${ceiling}`,
}
},
})
test('custom', () => { expect(100).toBeWithinRange(90, 110) })Snapshots
expect(data).toMatchSnapshot()
expect(data).toMatchInlineSnapshot(`{ "id": 1 }`)
await expect(result).toMatchFileSnapshot('./expected.json')
expect(() => { throw new Error('fail') }).toThrowErrorMatchingSnapshot()
// With hints
expect(header).toMatchSnapshot('header')
// Shape matching
expect(data).toMatchSnapshot({
id: expect.any(Number),
created: expect.any(Date),
})Update snapshots: vitest -u or press u in watch mode.
Custom Serializers
expect.addSnapshotSerializer({
test(val) { return val && typeof val.toJSON === 'function' },
serialize(val, config, indentation, depth, refs, printer) {
return printer(val.toJSON(), config, indentation, depth, refs)
},
})Coverage
vitest run --coveragedefineConfig({
test: {
coverage: {
provider: 'v8', // or 'istanbul'
enabled: true,
reporter: ['text', 'json', 'html'],
include: ['src/**/*.{ts,tsx}'],
exclude: ['node_modules/', 'tests/', '**/*.d.ts'],
all: true, // Report uncovered files
thresholds: { lines: 80, functions: 80, branches: 80, statements: 80 },
},
},
})Ignore code:
/* v8 ignore next -- @preserve */
function ignored() {}
/* istanbul ignore next -- @preserve */
function ignored() {}Filtering
vitest user # Files containing "user"
vitest src/user.test.ts:25 # Specific line
vitest -t "login" # Tests matching pattern
vitest --changed # Uncommitted changes
vitest --changed HEAD~1 # Since commit
vitest related src/utils.ts --run # Tests importing file
vitest --tags db # By tagtest('db test', { tags: ['db', 'slow'] }, async () => {})Concurrency
defineConfig({
test: {
fileParallelism: true,
maxWorkers: 4,
pool: 'threads', // 'threads', 'forks', 'vmThreads'
maxConcurrency: 5, // Max concurrent tests per file
isolate: true,
sequence: {
shuffle: true,
seed: 12345,
hooks: 'stack',
concurrent: true,
},
},
})describe.concurrent('parallel', () => {
test('test 1', async ({ expect }) => {})
test('test 2', async ({ expect }) => {})
})
describe.shuffle('random order', () => {})Related skills
How it compares
Pick vitest for Nuxt-aligned Vitest environment and multi-project setup; use Jest migration guides when the codebase is still locked to Jest APIs and plugins.
FAQ
Which environment should DOM tests use?
Set test.environment to jsdom in vitest.config.ts for DOM APIs; use node for pure logic.
Should I read every reference file?
No. Load only the reference matching your task such as mocking.md or config.md.
Where do Vue component test patterns live?
Use the vue skill for component testing; vitest covers the test runner setup and APIs.
Is Vitest safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.