Vitest представляет собой современный тестовый фреймворк, разработанный специально для тесной интеграции с Vite. В отличие от классических решений вроде Jest, Vitest использует инфраструктуру Vite напрямую: систему трансформации модулей, граф зависимостей, плагины и механизм ESM.
Основная идея Vitest заключается в устранении промежуточных слоёв между приложением и тестовой средой. Благодаря этому достигаются:
Vitest особенно эффективен в проектах на:
Базовая установка выполняется через npm:
npm install -D vitest
Для работы с DOM обычно дополнительно подключаются:
npm install -D jsdom @testing-library/dom
Для React:
npm install -D @testing-library/react
Для Vue:
npm install -D @vue/test-utils
Vitest может использовать существующий vite.config.js,
однако тестовые параметры чаще выносятся в отдельную секцию.
Пример:
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
test: {
globals: true,
environment: 'jsdom'
}
})
Параметр globals позволяет использовать глобальные
функции:
describe()
it()
expect()
beforeEach()
Без необходимости импортировать их вручную.
Пример простой функции:
// math.js
export function sum(a, b) {
return a + b
}
Тест:
// math.test.js
import { sum } from './math'
describe('sum', () => {
it('adds numbers', () => {
expect(sum(2, 3)).toBe(5)
})
})
Запуск:
npx vitest
Режим наблюдения:
npx vitest --watch
Vitest использует внутренние механизмы Vite:
За счёт этого тесты используют ту же цепочку обработки, что и основное приложение.
Это устраняет типичную проблему рассинхронизации между:
Vitest ориентирован на современный JavaScript и нативные ES-модули.
Пример:
import { userService } from './services/userService.js'
В отличие от старых тестовых раннеров, не требуется:
Vitest автоматически использует TypeScript через Vite.
Файл:
// calculator.ts
export function multiply(a: number, b: number): number {
return a * b
}
Тест:
import { multiply } from './calculator'
describe('multiply', () => {
it('multiplies values', () => {
expect(multiply(4, 5)).toBe(20)
})
})
Отдельная настройка TypeScript-компилятора обычно не требуется.
Vitest предоставляет familiar API, схожий с Jest.
Основные функции:
| API | Назначение |
|---|---|
| describe | группировка тестов |
| it/test | отдельный тест |
| expect | проверки |
| beforeEach | код перед тестом |
| afterEach | очистка |
| beforeAll | подготовка |
| afterAll | завершение |
expect(value).toBe(10)
expect(user).toEqual({
id: 1,
name: 'Alex'
})
expect(data).toBeTruthy()
expect(error).toBeFalsy()
expect(items).toContain('admin')
expect(() => {
throw new Error('Fail')
}).toThrow()
it('loads data', async () => {
const result = await fetchUsers()
expect(result.length).toBe(3)
})
await expect(loadUser()).rejects.toThrow()
await expect(getStatus()).resolves.toBe('ok')
Vitest предоставляет встроенную систему мокирования через
vi.
const fn = vi.fn()
fn('hello')
expect(fn).toHaveBeenCalled()
expect(fn).toHaveBeenCalledWith('hello')
const fn = vi.fn(() => 42)
expect(fn()).toBe(42)
Пример:
vi.mock('./api', () => {
return {
getUsers: vi.fn(() => [])
}
})
Vitest перехватывает импорт модуля и подменяет его содержимое.
Spy позволяет отслеживать вызовы существующих методов.
const spy = vi.spyOn(console, 'log')
console.log('test')
expect(spy).toHaveBeenCalled()
Восстановление:
spy.mockRestore()
Тестирование таймеров:
vi.useFakeTimers()
const callback = vi.fn()
setTimeout(callback, 1000)
vi.advanceTimersByTime(1000)
expect(callback).toHaveBeenCalled()
Vitest может работать с браузерным окружением через
jsdom.
Конфигурация:
test: {
environment: 'jsdom'
}
Пример:
document.body.innerHTML = `
<button id="btn">Click</button>
`
const button = document.querySelector('#btn')
expect(button.textContent).toBe('Click')
Часто Vitest используется вместе с Testing Library.
Пример React-компонента:
function Counter() {
return <button>Increment</button>
}
Тест:
import { render, screen } from '@testing-library/react'
it('renders button', () => {
render(<Counter />)
expect(screen.getByText('Increment')).toBeDefined()
})
Vitest поддерживает snapshots.
expect(data).toMatchSnapshot()
При первом запуске создаётся snapshot-файл.
Повторные запуски сравнивают текущее состояние со снимком.
expect(user).toMatchInlineSnapshot(`
{
"id": 1,
"name": "Alex",
}
`)
Vitest поддерживает coverage через V8.
Установка:
npm install -D @vitest/coverage-v8
Конфигурация:
test: {
coverage: {
provider: 'v8'
}
}
Запуск:
npx vitest run --coverage
Каждый тест должен быть независимым.
Пример очистки:
beforeEach(() => {
localStorage.clear()
})
Изоляция предотвращает:
Vitest поддерживает многопоточность.
Тесты автоматически распределяются между worker-процессами.
Это особенно важно для:
Иногда требуется отключить параллелизм:
test: {
sequence: {
concurrent: false
}
}
Запуск конкретного файла:
npx vitest math.test.js
Запуск по имени:
npx vitest -t "adds numbers"
it.only('works', () => {
})
it.skip('temporary disabled', () => {
})
Vitest имеет встроенный интерфейс:
npx vitest --ui
Требуется установка:
npm install -D @vitest/ui
Интерфейс показывает:
Vitest поддерживает запуск тестов в реальном браузере.
Пример:
test: {
browser: {
enabled: true,
name: 'chrome'
}
}
Это полезно для:
Запуск в CI:
npx vitest run
Обычно используется вместе с:
name: tests
on:
push:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install
- run: npx vitest run
Vitest особенно тесно связан с Vue-экосистемой.
Пример:
<script setup>
defineProps({
title: String
})
</script>
<template>
<h1>{{ title }}</h1>
</template>
Тест:
import { mount } from '@vue/test-utils'
import Component from './Component.vue'
it('renders title', () => {
const wrapper = mount(Component, {
props: {
title: 'Hello'
}
})
expect(wrapper.text()).toContain('Hello')
})
Пример React-хука:
import { useState } from 'react'
export function useCounter() {
const [count, setCount] = useState(0)
return {
count,
increment: () => setCount(v => v + 1)
}
}
Тестирование:
import { renderHook, act } from '@testing-library/react'
it('increments counter', () => {
const { result } = renderHook(() => useCounter())
act(() => {
result.current.increment()
})
expect(result.current.count).toBe(1)
})
Vitest использует alias из Vite.
resolve: {
alias: {
'@': '/src'
}
}
Тест:
import { api } from '@/services/api'
Дополнительная настройка не требуется.
Общие настройки:
test: {
setupFiles: './tests/setup.js'
}
Пример:
import '@testing-library/jest-dom'
Автоматическая очистка:
test: {
clearMocks: true,
restoreMocks: true
}
Это снижает вероятность утечки состояния между тестами.
Vitest поддерживает benchmarking.
Пример:
import { bench } from 'vitest'
bench('sort array', () => {
;[...Array(1000)]
.map(() => Math.random())
.sort()
})
| Vitest | Jest |
|---|---|
| Нативный ESM | CommonJS-истоки |
| Интеграция с Vite | Отдельная система |
| Быстрый cold start | Более тяжёлый запуск |
| Использует Vite pipeline | Собственный pipeline |
| Современная архитектура | Legacy-подходы |
| Отличная поддержка TS | Требует больше конфигурации |
Высокая скорость достигается благодаря:
На крупных проектах разница между Jest и Vitest может быть весьма заметной.
Распространённый вариант:
src/
tests/
Либо co-location:
Button.jsx
Button.test.jsx
Хорошие названия:
it('returns empty array for guests')
Плохие:
it('test 1')
Название должно описывать поведение, а не реализацию.
Vitest подходит для:
Изолированная проверка функций.
Проверка взаимодействия модулей.
Тестирование UI-компонентов.
Несмотря на преимущества, существуют особенности:
Vitest хорошо работает с monorepo:
packages/
core/
ui/
api/
Каждый пакет может иметь собственную конфигурацию либо использовать общую базовую.
Рекомендуемый подход:
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true
}
})
Использование vitest/config улучшает типизацию и
автодополнение.
Запуск:
npx vitest --inspect
Позволяет подключать:
Одно из ключевых преимуществ Vitest — мгновенное обновление тестов при изменении файлов.
При сохранении:
Это существенно ускоряет TDD-процесс.
Vitest особенно эффективен в проектах с:
Единая инфраструктура разработки и тестирования снижает сложность поддержки проекта и уменьшает объём конфигурации.