раздел 29
Тестирование
Сквозная инженерная дисциплина: зачем тесты, чем виды отличаются, что тестировать, как оценивать честность набора. Один проект-касса проходит все уровни, от контракта чистой функции до E2E и мутационного тестирования. Центральный сюжет: тест пишет человек, реализацию пишет агент, честность теста проверяет Stryker.
Начать раздел
Хочу пройти
Не получилось изменить список. Попробуй ещё раз.
Без входа у каждого урока видно начало. Войди, чтобы читать целиком, отмечать пройденное и решать задачи. войти
Уроки раздела
- Зачем тесты: контракты, инварианты и тест как документация
- Виды тестов и что прячет coverage
- Что тестировать: поведение, а не реализацию
- Эвристики качества теста и антипаттерны
- Пирамида против трофея и как ИИ меняет экономику тестов
- Стек тестов: раннер, ассерты и дубли зависимостей
- Фикстуры и тестовые данные: factory вместо god-объекта
- Параллелизм и изоляция: почему тест зелёный локально и красный в CI
- Тесты в CI: кэш, шардинг, coverage gate и удобство работы
- Mutation testing: честный прибор и приёмка ИИ-тестов
- Тест как ТЗ для агента
- Анатомия юнит-теста: скорость, изоляция, детерминизм
- Дубли: классификация, овермок и in-memory fakes
- Подмена зависимостей: vi.mock, ручной DI, Effect-слой
- Property-based и snapshot: тысяча проверяющих и ловушка approve
- Что такое E2E: пройти кассу глазами клиента
- Интеграционные тесты: сколько настоящего в проверке
- Проблемы E2E: состояние, flakiness и Page Object
- Рефакторинг под тестируемость: швы, ядро и оболочка
- API и контрактное тестирование: схема как единый договор
- Внедрение тестов в проект без тестов: сначала зафиксируй, потом меняй
- Ошибки в тестах: галерея антипаттернов и чеклист для ревью
- Карантин и flaky-тесты: пожарная тревога, которая врёт
- Тестирование с ИИ: полная петля от теста к честному mutation score
- Метрики тестирования: от зелёного теста к честной приборной панели
- Формальная верификация: от property-теста к доказательству в Lean
Понятия курса
Понятия разобраны внутри уроков. Нажми на понятие, чтобы открыть урок, где оно объясняется.
Принципы
- Виды тестовUnit, integration, E2E плюс contract, property, snapshot, mutation как отдельные оси. Лестница стоимость против уверенности.урок 2 · Виды тестов и что прячет coverageопираются: урок 5, урок 6, урок 7, урок 8, урок 9, урок 16
- Зачем тестыТест как страховка при рефакторинге и как исполняемая документация, а не как ритуал "проверить, что работает".урок 1 · Зачем тесты: контракты, инварианты и тест как документация
- Контракты и инвариантыКонтракт: предусловие, постусловие, что считается нарушением. Инвариант: свойство, истинное после любой операции. Зелёный тест слабее, чем сохранённый инвариант.урок 1 · Зачем тесты: контракты, инварианты и тест как документацияопираются: урок 2, урок 3, урок 15, урок 26
- Метрики покрытияLine, branch, function, statement coverage: что каждая считает и где разница.урок 2 · Виды тестов и что прячет coverage
- Тест как документацияТест читается как спецификация и не врёт, потому что запускается.урок 1 · Зачем тесты: контракты, инварианты и тест как документация
- Антипаттерны тестаВечнозелёный тест, тест-тавтология, тест, повторяющий реализацию в assert, мок, проверяющий мок.урок 4 · Эвристики качества теста и антипаттерныопираются: урок 22
- Граничные случаиНоль, отрицательное, переполнение, пустое. Где живут баги, которые happy-path не ловит.урок 3 · Что тестировать: поведение, а не реализациюопираются: урок 11, урок 15
- ИИ меняет экономику тестовАгент пишет юниты почти бесплатно, mutation testing возвращает им доверие. Баланс набора снова едет к основанию пирамиды.урок 5 · Пирамида против трофея и как ИИ меняет экономику тестовопираются: урок 10, урок 11
- Пирамида против трофеяПирамида Фаулера, кубок-трофей Доддса, рожок мороженого как антипаттерн. Три формы набора и что каждая значит на практике.урок 5 · Пирамида против трофея и как ИИ меняет экономику тестовопираются: урок 16, урок 22
- Поведение, не реализацияТестируем поведение через публичный API. Тест на деталь реализации краснеет зря, особенно когда код переписывает агент.урок 3 · Что тестировать: поведение, а не реализациюопираются: урок 4, урок 11, урок 12, урок 13
- Слепые зоны coverageСтрока исполнена не значит поведение проверено. Coverage не видит ассерты и ветки данных. Интрига: честный инструмент впереди.урок 2 · Виды тестов и что прячет coverageопираются: урок 3, урок 4, урок 10
- Стратегия от контекстаLegacy без тестов, greenfield, AI-assisted: для каждого своя форма набора. Контекст решает, а не догма.урок 5 · Пирамида против трофея и как ИИ меняет экономику тестов
- Четыре столпаЗащита от регрессий, устойчивость к рефакторингу, быстрая обратная связь, поддерживаемость. По Хорикову.урок 4 · Эвристики качества теста и антипаттерны
- Эвристики качества тестаПадает ли тест, если сломать код. Читается ли как спецификация. Один ли повод падать. Не дублирует ли продакшн-логику.урок 4 · Эвристики качества теста и антипаттерныопираются: урок 5, урок 6, урок 7, урок 8, урок 9, урок 10
- ROI тестовГде тесты дают максимум: деньги, авторизация, парсинг, граничные значения. Где почти никогда: геттеры, тривиальный glue.урок 3 · Что тестировать: поведение, а не реализациюопираются: урок 16, урок 21
Инфраструктура
- @effect/vitestit.effect принимает Effect телом теста и убирает runPromise-boilerplate. Ошибку проверяют через Effect.either по тегу, а не toThrow по строке.урок 6 · Стек тестов: раннер, ассерты и дубли зависимостейопираются: урок 12
- Состав тестового стекаРаннер запускает, библиотека ассертов судит, дубли подменяют зависимости. Три цеха одной фабрики.урок 6 · Стек тестов: раннер, ассерты и дубли зависимостей
- Удобство работы с тестамиWatch mode, фокус на одном тесте по имени, инлайн-результаты и дебаг в IDE укорачивают цикл обратной связи до секунд.урок 9 · Тесты в CI: кэш, шардинг, coverage gate и удобство работы
- ФикстурыПодготовленное состояние для теста. Хрупкая god-фикстура связывает несвязанные тесты и роняет полнабора от одного изменения поля.урок 7 · Фикстуры и тестовые данные: factory вместо god-объекта
- CI-пайплайнЛента ворот: линт, типы, тесты, покрытие. Дешёвые шаги раньше дорогих. Кэш зависимостей и шардинг режут время прогона.урок 9 · Тесты в CI: кэш, шардинг, coverage gate и удобство работыопираются: урок 25
- Coverage gateПорог покрытия в PR: ниже процента сборка красная. Порог обычно не 100, чтобы не плодить пустые тесты. Высокое покрытие это ещё не честный набор.урок 9 · Тесты в CI: кэш, шардинг, coverage gate и удобство работыопираются: урок 25
- Factory и builderFactory отдаёт объект с дефолтами и overrides, в тесте видно только важное поле. Builder собирает составной объект пошагово через fluent-API.урок 7 · Фикстуры и тестовые данные: factory вместо god-объекта
- Seed для интеграционныхМинимальный детерминированный набор данных в базе перед интеграционным тестом. Мост к изоляции.урок 7 · Фикстуры и тестовые данные: factory вместо god-объектаопираются: урок 18
- Stub, mock, fakeДубли различаются по умениям: stub отдаёт ответ, spy помнит вызовы, mock проверяет вызов, fake это рабочая мини-реализация. В Effect место дубля занимает тестовый Layer.урок 6 · Стек тестов: раннер, ассерты и дубли зависимостейопираются: урок 13
- VitestРаннер, ассерты, дубли и coverage в одном пакете. Понимает TypeScript и ESM, берёт конфиг от Vite.урок 6 · Стек тестов: раннер, ассерты и дубли зависимостей
- Изоляция тестовТри стратегии: транзакция с откатом, схема на воркер, контейнер на воркер. В Effect изоляция получается по построению через слой-на-тест.урок 8 · Параллелизм и изоляция: почему тест зелёный локально и красный в CIопираются: урок 17, урок 18, урок 23
- Параллелизм тестовРаннер гоняет тесты в воркерах, единица обычно файл. Гонка возникает на общем изменяемом состоянии: зелёный локально, красный в CI.урок 8 · Параллелизм и изоляция: почему тест зелёный локально и красный в CI
- Приёмка ИИ-тестовMutation score как приёмка для ИИ-сгенерированных тестов: декоративные зелёные тесты всплывают, потому что не убивают мутантов.урок 10 · Mutation testing: честный прибор и приёмка ИИ-тестов
- Ресурсы в ScopeКонтейнер или коннект живёт в Scope, it.scoped закрывает его сам после теста. Забытого teardown не бывает.урок 8 · Параллелизм и изоляция: почему тест зелёный локально и красный в CI
- Mutation scoreДоля убитых мутантов. Меряет силу ассертов, а не исполнение строк. Coverage 100 и низкий mutation score это норма для пустого набора.урок 10 · Mutation testing: честный прибор и приёмка ИИ-тестовопираются: урок 24, урок 25
- Mutation testingИнструмент сажает в код мутантов и проверяет, ловит ли их набор. Автоматизация эвристики «сломай код». Честный прибор, обещанный с урока про coverage.урок 10 · Mutation testing: честный прибор и приёмка ИИ-тестов
- testcontainersРеальный Postgres в контейнере для интеграционных тестов, оформленный как scoped-Layer.урок 8 · Параллелизм и изоляция: почему тест зелёный локально и красный в CIопираются: урок 17
Unit-тестирование
- Детерминизм тестаЗашитые Date.now и Math.random делают тест зависимым от часов и недетерминированным. Лечится контролем времени и случайности через сервисы.урок 12 · Анатомия юнит-теста: скорость, изоляция, детерминизмопираются: урок 14
- Неоднозначное ТЗЕсли тест не задал поведение на границе, агент выбирает свою трактовку (округление вниз против банковского). Тест, различающий ветки, и есть уточнение спецификации.урок 11 · Тест как ТЗ для агента
- Реализация агентом по тестуЧеловек пишет тесты, агент пишет реализацию. Петля: таблица случаев, агент реализует, red/green, уточнение на расхождении. Полный цикл с Stryker разворачивается в блоке внедрения.урок 11 · Тест как ТЗ для агентаопираются: урок 24
- Свойства юнит-тестаЧто делает тест юнит-тестом: скорость в миллисекундах, изоляция (нет сети, БД, файлов, времени), детерминизм (один и тот же результат всегда).урок 12 · Анатомия юнит-теста: скорость, изоляция, детерминизм
- Тест как ТЗТаблица «вход, выход» точнее прозы и сразу исполняема. Хорошее ТЗ это конкретные входы и выходы, покрытые границы, отсутствие деталей реализации в assert.урок 11 · Тест как ТЗ для агентаопираются: урок 12, урок 15
- Arrange-Act-AssertТри такта теста: подготовь, сделай, проверь. Явные секции делают тест читаемым как абзац документации и оставляют один повод для падения.урок 12 · Анатомия юнит-теста: скорость, изоляция, детерминизм
- Snapshot-тестыПолезны для стабильной маленькой сериализации. Привычка жать «approve all» без чтения диффа превращает snapshot в штамп, который ничего не защищает.урок 15 · Property-based и snapshot: тысяча проверяющих и ловушка approve
- Внедрение зависимостейЗависимость как аргумент или поле вместо хардкода. Розетка против припаянного провода: подменить зависимость для теста можно, только если есть шов.урок 14 · Подмена зависимостей: vi.mock, ручной DI, Effect-слойопираются: урок 19
- Классификация дублейDummy, stub, spy, mock, fake: пять видов дублёров по умениям. Когда что уместно и почему mock привязывает тест к реализации.урок 13 · Дубли: классификация, овермок и in-memory fakes
- ОвермокКогда замокано всё, тест проверяет, что код вызвал то, что вызвал, и краснеет от любого рефактора. Классическая школа против лондонской.урок 13 · Дубли: классификация, овермок и in-memory fakesопираются: урок 22
- Подмена это композицияВ Effect тест это Effect.provide(TestLayer), прод это Effect.provide(LiveLayer): один механизм. Архитектура, удобная для теста, совпадает с хорошей по построению.урок 14 · Подмена зависимостей: vi.mock, ручной DI, Effect-слой
- Тестовые Layer в EffectЗависимость это сервис в Context, подмена это другой Layer. TestRatesApi.layer такой же Layer, как боевой LiveRatesApi.layer: мок-инфраструктура не живёт отдельно от приложения.урок 14 · Подмена зависимостей: vi.mock, ручной DI, Effect-слойопираются: урок 19
- In-memory fakesРабочая мини-реализация вместо горы моков. В Effect-проекте fake это Layer того же сервиса, что и боевой: правильная альтернатива овермоку и механизм подмены это одно и то же.урок 13 · Дубли: классификация, овермок и in-memory fakesопираются: урок 14
- Module mockingvi.mock подменяет модуль целиком. Работает без рефакторинга, но хрупко: привязано к путям импорта и легко мокнуть лишнее.урок 14 · Подмена зависимостей: vi.mock, ручной DI, Effect-слой
- Property-based тестыВместо ручных примеров проверяем свойство на тысячах случайных входов. Round-trip и инварианты это главные кандидаты. Находит границы, которые агент не предусмотрел.урок 15 · Property-based и snapshot: тысяча проверяющих и ловушка approveопираются: урок 26
- ShrinkingНайдя падающий вход, инструмент сам сжимает его до минимального контрпримера. Вместо случайного огромного входа получаешь короткий понятный.урок 15 · Property-based и snapshot: тысяча проверяющих и ловушка approve
- TestClock и TestRandomClock и Random как сервисы, в тесте TestClock.adjust и TestRandom с seed через TestContext. Виртуальное время мгновенно проматывает таймауты без реального sleep.урок 12 · Анатомия юнит-теста: скорость, изоляция, детерминизмопираются: урок 13
E2E и интеграция
- Выбор уровняДерево решений: нужен браузер это E2E, нужна реальная БД или HTTP это интеграция, чистая логика это юнит. Бери самый дешёвый уровень, на котором проверка ещё осмысленна.урок 17 · Интеграционные тесты: сколько настоящего в проверке
- Граница уровней тестаУровень это про число настоящих зависимостей, а не про размер. Юнит: всё подменено. Интеграция: настоящая БД. E2E: настоящее всё, включая браузер.урок 17 · Интеграционные тесты: сколько настоящего в проверке
- Интеграционные тестыРеальная БД или HTTP, но без браузера. Серверные факты (запись в БД, накопление, авторизация) проверяются дешевле и стабильнее, чем кликами в E2E.урок 17 · Интеграционные тесты: сколько настоящего в проверкеопираются: урок 20
- Что обязательно покрывать E2EДеньги, авторизация, критический happy-path. Перебор edge, валидация полей и форматы дешевле проверяются юнитом и API-тестом, а не E2E.урок 16 · Что такое E2E: пройти кассу глазами клиента
- Что такое E2EПроверка всего приложения целиком, от браузера до БД, ничего не подменяя. Самый честный и самый дорогой уровень, поэтому его держат малым.урок 16 · Что такое E2E: пройти кассу глазами клиентаопираются: урок 17, урок 18
- E2E дополняет, не заменяетE2E проверяет, что стек склеен, юнит ловит ветку логики. Только E2E это рожок мороженого: медленно и не видно, где сломалось. Каждый уровень сужает место поломки.урок 16 · Что такое E2E: пройти кассу глазами клиента
- Контрактное тестированиеВ Effect форма ответа это один Schema: тип, валидатор и контракт сразу, в общем пакете для consumer и provider. Один источник правды формы ловит рассинхрон раньше E2E и прода.урок 20 · API и контрактное тестирование: схема как единый договор
- Непроверяемый кодРабочий код бывает непроверяемым: зашитые fetch и Date.now, побочки посреди логики, модульное состояние, нет шва. В Effect зашитую зависимость видно по её отсутствию в сигнатуре R.урок 19 · Рефакторинг под тестируемость: швы, ядро и оболочкаопираются: урок 21
- Порты и адаптерыВнешняя зависимость это порт, реализация приходит адаптером снаружи (HTTP в проде, in-memory в тесте). В Effect прод и тест это одна программа с разным Effect.provide. Тестируемость это следствие хорошей архитектуры.урок 19 · Рефакторинг под тестируемость: швы, ядро и оболочка
- Состояние в E2EE2E на настоящей БД не изолирован, пока этим не займёшься. Свежая БД на прогон, уникальные данные на тест, seed через API, готовность сервера ждут опросом, а не таймингом.урок 18 · Проблемы E2E: состояние, flakiness и Page Objectопираются: урок 23
- API-тестированиеHTTP-слой проверяется на границе: статусы, коды ошибок, форма тела, авторизация. Успешное тело декодируют схемой, ошибку проверяют по тегу. 400 это кривая форма, 422 это правило домена.урок 20 · API и контрактное тестирование: схема как единый договор
- Consumer-driven contractsДоговор пишет потребитель: consumer описывает форму, которую ждёт, provider обязан соответствовать. В монорепо это общий Schema-пакет, в микросервисах Pact. Контракт краснеет раньше E2E.урок 20 · API и контрактное тестирование: схема как единый договор
- Flakiness и ожидание состоянияГлавный источник flaky это время. waitForTimeout это гонка под видом паузы. Лечится ожиданием состояния через web-first assertion и селекторами по роли и data-testid.урок 18 · Проблемы E2E: состояние, flakiness и Page Objectопираются: урок 23
- Functional core, imperative shellЧистое ядро принимает входы аргументами и тестируется юнитами мгновенно. Тонкая оболочка собирает время и сеть и зовёт ядро. Передаёшь время аргументом, тест детерминирован сам собой.урок 19 · Рефакторинг под тестируемость: швы, ядро и оболочкаопираются: урок 21
- Page Object ModelСелекторы и действия страницы собраны в один класс. Тест говорит login и pay, не зная про data-testid. Правка вёрстки это правка одного объекта. Сессия переиспользуется через storageState.урок 18 · Проблемы E2E: состояние, flakiness и Page Object
Внедрение
- Метрики как gatesCoverage-gate и mutation-gate как блокеры мерджа, карантин как не-блокер. Объективный критерий качества PR встроен в CI, а не остаётся пожеланием на ревью.урок 25 · Метрики тестирования: от зелёного теста к честной приборной панели
- Метрики набора тестовПриборная панель, а не одна цифра: coverage как тренд, flaky rate, mutation score, время прогона, доля карантина. Развязка сквозной линии от зелёного теста к честным метрикам.урок 25 · Метрики тестирования: от зелёного теста к честной приборной панели
- Осмысленные порогиCoverage как «не падай ниже тренда», mutation score как реальный порог качества, flaky rate как сигнал доверия, время как бюджет фидбэка. Не одна абсолютная цель.урок 25 · Метрики тестирования: от зелёного теста к честной приборной панели
- Ошибки в тестахГалерея антипаттернов с минимальным до и после: тест на реализацию, god-фикстура, тавтология, дублирование продакшн-логики в assert, лишние моки, тест без причины падать.урок 22 · Ошибки в тестах: галерея антипаттернов и чеклист для ревью
- Правило бойскаутаПокрытие наращивают по ходу обычной работы: тронул файл по задаче, оставил его чуть покрытее. Без отдельного тестового спринта, который останавливает разработку.урок 21 · Внедрение тестов в проект без тестов: сначала зафиксируй, потом меняй
- Приоритет по критическим путямВнедрение тестов в проект без тестов начинают не с геттеров, а с критических путей: деньги и авторизация сначала. Карта путей, отсортированная по риску.урок 21 · Внедрение тестов в проект без тестов: сначала зафиксируй, потом меняй
- Промпт-стратегии для тестовЧто просить у агента: тестируй поведение через публичный API, покрой границы X и Y, не мокай то, что можно вызвать по-настоящему. Антипромпт «напиши тесты для файла» без критериев даёт тавтологии.урок 24 · Тестирование с ИИ: полная петля от теста к честному mutation score
- Рожок мороженогоПеревёрнутая пирамида: много E2E, мало юнитов. Медленно, хрупко, не видно, где сломалось. Выправляется переносом логики с E2E на юнит-уровень.урок 22 · Ошибки в тестах: галерея антипаттернов и чеклист для ревью
- Характеризующие тестыХарактеризующий тест не судит, правильно ли поведение, он его фотографирует. Записываем фактический вывод legacy в assert, чтобы любое изменение стало видно перед рефакторингом.урок 21 · Внедрение тестов в проект без тестов: сначала зафиксируй, потом меняй
- Чеклист ревью тестаДесять пунктов, по которым проходишь тест перед мерджем: поведение не реализация, один повод падать, нет god-фикстуры, нет тавтологии, детерминизм. Фильтр против агентских антипаттернов.урок 22 · Ошибки в тестах: галерея антипаттернов и чеклист для ревью
- КарантинПомеченный flaky уходит в non-blocking-группу: виден, но не блокирует мердж. Заводим тикет, не удаляем. Карантин это срок на починку, а не кладбище.урок 23 · Карантин и flaky-тесты: пожарная тревога, которая врёт
- Обнаружение плохих ИИ-тестовНизкий mutation score, тавтологии, моки на всё, snapshot без чтения диффа. Доказательство через «сломай код» или Stryker, а не через зелёный прогон.урок 24 · Тестирование с ИИ: полная петля от теста к честному mutation score
- Поиск корневой причиныПрогон в цикле для воспроизведения, фиксация seed и порядка, поиск общего состояния. Устраняем источник нестабильности (гонка, время, порядок, сеть), а не добавляем retry.урок 23 · Карантин и flaky-тесты: пожарная тревога, которая врёт
- Чем опасны flaky-тестыFlaky опаснее отсутствия теста: пожарная тревога, которая врёт зря, заставляет команду игнорировать красное и пропустить настоящий пожар. Разрушает доверие ко всему набору.урок 23 · Карантин и flaky-тесты: пожарная тревога, которая врёт
- AI-workflow тестированияПолная петля: тест вручную, агент реализует, Stryker проверяет честность, уточняем тест по выжившим мутантам, повторяем. Цикл сходится к высокому mutation score.урок 24 · Тестирование с ИИ: полная петля от теста к честному mutation score
Формальная верификация
- Проверка моделей и TLA+Проверка модели перебирает чередования и ищет нарушение, а не доказывает теорему. TLA+ ловит гонку двух конкурентных списаний, уводящую баланс в минус. Доказательство закрывает один поток, проверка модели конкурентность.урок 26 · Формальная верификация: от property-теста к доказательству в Lean
- Типы как доказательстваCurry-Howard: тип это теорема, программа это доказательство. Branded-типы и тотальность из 04-ts это уже формальная верификация в малом. Непрерывность от типов к Lean.урок 26 · Формальная верификация: от property-теста к доказательству в Lean
- Формальная верификацияГоризонт за тестами: тест проверяет на конечном числе входов, доказательство гарантирует на всех. Лестница строгости: пример, property, тип, доказательство. Окупается на деньгах, крипто, протоколах.урок 26 · Формальная верификация: от property-теста к доказательству в Lean
- Доказательства в LeanИнвариант баланса, доказанный в Lean 4 для всех входов сразу, а не на выборке. Та же касса, что в домене, проверена не примером и не property, а машинно проверенным доказательством.урок 26 · Формальная верификация: от property-теста к доказательству в Lean