Стек тестов: раннер, ассерты и дубли зависимостей
Сцена · три цеха одной фабрики
На фабрике тестов работают три цеха. Первый запускает: находит файлы, гоняет их в изоляции, собирает отчёт. Это раннер. Второй судит: сравнивает то, что получилось, с тем, что ждали, и понятно объясняет расхождение. Это библиотека ассертов. Третий подменяет соседей: пока мы проверяем одну деталь, остальные заменяем безопасными заглушками. Это тестовые дубли.
В прошлом блоке мы думали про тесты головой. Теперь ставим инструмент. Хорошая новость: Vitest объединяет все три цеха в одном пакете, и для эффектного кода поверх него ложится тонкая надстройка @effect/vitest.
Карта урока · что заберёшь
- разложишь тестовый стек на три части: раннер, ассерты, дубли;
- поймёшь, почему в проекте стоит Vitest, а не сборка из трёх пакетов;
- разложишь дубли по полкам: dummy, stub, spy, mock, fake;
- впервые проверишь тегированную ошибку как значение, через
Effect.result, а не черезtoThrow.
Раздел 1 · из чего собран стек
Идея словами
Исторически это были три отдельных пакета: один раннер, к нему библиотека ассертов, к ним мок-фреймворк. Их приходилось склеивать и согласовывать версии. Vitest собирает всё в одном месте: запуск, expect, vi.fn, coverage, watch. Он понимает TypeScript и ESM из коробки и берёт конфиг от Vite, поэтому тесты видят проект так же, как его видит сборка.
Шаг 1 · самый простой тест
Чистый домен Kassa проверяется обычным Vitest без всякой обвязки. Один вход, один выход.
import { describe, expect, it } from 'vitest';
import { validatePayment } from '../src/validate-payment.ts';
import { makeAccount, makePayment } from './support/factories.ts';
describe('validatePayment', () => {
it('пропускает платёж в пределах остатка', () => {
const reasons = validatePayment(makeAccount({ balance: 100 }), makePayment(30));
expect(reasons).toEqual([]);
});
});
describe группирует, it это один случай, expect(...).toEqual(...) это работа цеха ассертов. Запусти pnpm test в пакете домена кассы и увидишь зелёную строку.
Что взять с собой
- тестовый стек это раннер, ассерты, дубли;
- Vitest объединяет всё и понимает TS и ESM без настройки;
- чистую функцию проверяют простым
itбез обвязки.
Цех ассертов легко принять за магию, пока не соберёшь его сам. Ниже редактор: напиши свой expect на три матчера, с понятными сообщениями о провале и рабочим .not. Тесты гоняются прямо в браузере, ставить ничего не нужно.
Раздел 2 · карта дублей
Идея словами
Когда функция зависит от соседа (часов, базы, чужого API), в тесте соседа заменяют дублем. Дубли различаются не названием ради названия, а тем, что они умеют: что отдают, помнят ли вызовы, проверяют ли вызов сами, есть ли в них рабочая логика.
Картинка · переключи дубль
Переключай вид дубля и смотри на таблицу свойств. Заметь главное: чем правее в ряду dummy, stub, spy, mock, fake, тем больше дубль знает про взаимодействие. Mock самый требовательный: он сам валит тест, если его позвали не так. Fake самый честный: это рабочая мини-реализация.
Шаг 1 · stub часов через vi.fn
Самый частый дубль это stub: заранее заданный ответ. Подменим часы, чтобы тест не зависел от реального времени.
import { expect, it, vi } from 'vitest';
it('ставит время операции из часов', () => {
const clock = { now: vi.fn(() => 1_700_000_000_000) };
const op = recordOperation(clock, makePayment(30));
expect(op.at).toBe(1_700_000_000_000);
});
vi.fn создаёт функцию-заглушку с заданным возвратом. Тест больше не зависит от того, который сейчас час.
Когда код пишет агент
Агент любит замокать всё подряд: и часы, и репозиторий, и саму проверяемую логику. Получается тест, который проверяет моки, а не код. Правило простое: подменяй только то, что мешает (время, сеть, БД), а домен оставляй настоящим. К избыточному мокированию вернёмся в блоке про unit-тесты отдельным уроком.
Что взять с собой
- дубли различаются по умениям, а не по названию;
- stub отдаёт готовый ответ, mock ещё и проверяет вызов, fake это рабочая мини-реализация;
- подменяй границы, домен оставляй настоящим.
дальше после входа
Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.
Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.