Vitest: unit-тесты
describe/test/expect, правильные матчеры, моки и поддельные таймеры.
Обновлено
Что такое Vitest
Vitest — тестраннер: находит файлы тестов (*.test.ts), запускает их и сообщает, что прошло, а что нет. Он быстрый, понимает TypeScript и ESM без настройки, а его API совместим с Jest (самым известным раннером).
npm i -D vitest
npx vitest # режим watch — перезапуск при каждом сохранении
npx vitest run # однократный запуск (CI)Структура
// src/shared/lib/price.test.ts
import { describe, expect, test } from 'vitest'
import { applyDiscount } from './price'
describe('applyDiscount', () => {
test('WELCOME10 снижает на 10%', () => {
expect(applyDiscount(100, 'WELCOME10')).toBe(90)
})
test('неизвестный код не меняет цену', () => {
expect(applyDiscount(100, 'NOPE')).toBe(100)
})
})describeгруппирует связанные тесты.test(илиit) — один случай.expect(значение).матчер(ожидаемое)— проверка.
Кладите тест рядом с проверяемым файлом (colocation) — легко найти, переезжает вместе с ним.
Матчеры — виды и когда
| Матчер | Сравнивает | Когда |
|---|---|---|
toBe(x) |
идентичность (Object.is) |
примитивы: числа, строки, boolean |
toEqual(x) |
содержимое, рекурсивно | объекты и массивы |
toStrictEqual(x) |
содержимое + undefined + класс |
когда важна точность |
toBeTruthy() / toBeNull() / toBeUndefined() |
особые значения | — |
toContain(x) |
элемент в массиве / подстрока | списки, тексты |
toHaveLength(n) |
длина | списки |
toThrow(msg?) |
функция бросает ошибку | валидация |
resolves / rejects |
Promise | асинхронный код |
expect({ a: 1 }).toBe({ a: 1 }) // ✗ разные объекты
expect({ a: 1 }).toEqual({ a: 1 }) // ✓ одинаковое содержимое
expect(() => parseAge(-1)).toThrow('Invalid age')
await expect(loadUser(1)).resolves.toEqual({ id: 1 })Моки
Мок — фальшивая функция, которая заменяет зависимость (API, время, случайность), чтобы тест был быстрым и детерминированным.
import { vi } from 'vitest'
const onSave = vi.fn() // функция-шпион
render(<Form onSave={onSave} />)
// ... пользователь отправляет форму
expect(onSave).toHaveBeenCalledWith({ email: 'a@x.com' })
vi.useFakeTimers() // вы управляете временем
vi.advanceTimersByTime(300) // например, для debounce| Вид | Что делает |
|---|---|
vi.fn() |
функция, записывающая вызовы |
vi.spyOn(obj, 'method') |
шпионит за существующим методом |
vi.mock('./api') |
подменяет целый модуль |
vi.useFakeTimers() |
управляемое время |
Не злоупотребляйте моками: чем больше вы подменяете, тем меньше реальности проверяете. Для HTTP-запросов MSW перехватывает их на уровне сети — код остаётся нетронутым.
Настройка с Next.js
У Next есть официальный гайд по Vitest (с @vitejs/plugin-react и jsdom для компонентов). Внимание: асинхронные Server Components пока плохо тестируются unit-тестами — для них используйте E2E.
Коротко
- Vitest запускает
*.test.ts;describe/test/expect. toBeдля примитивов,toEqualдля объектов.vi.fnи поддельные таймеры для зависимостей; подменяйте как можно меньше.