Инженерная практика
TDD: построй набор, который заметит баг
Читать про качество тестов — не то же, что построить набор, заслуживающий доверия. Возьми небольшой, но богатый на границы модуль, веди его test-first, закали техниками юнита, затем докажи, что набор настоящий, измерив, заметит ли он баг — с доказательствами на каждом шаге.
Преврати ментальную модель юнита в повторяемый инженерный цикл: проектируй под давлением test-first, применяй правило границ к дублёрам, добавь property там, где примеры слабее всего, и используй mutation score (а не покрытие), чтобы доказать, что набор поймает регрессию.
Построй небольшой доменный модуль test-first (движок денег/скидок, правило пересечения дат или rate-limit, либо парсер на твой выбор), затем докажи качество набора тестов через mutation score выше цели — а не число покрытия — применяя правило границ, property-тест и задокументированное решение по стабильности спеки.
- Таблица до/после для модуля: покрытие строк, mutation score и число выживших — измеренные инструментом, а не на глаз — с mutation score на критичной логике выше заявленной цели (например ≥85%).
- История git (или эквивалентный лог) показывает порядок red-green-refactor: падающий тест, закоммиченный до своей реализации, хотя бы для одного правила.
- Однострочное обоснование на каждый дублёр, показывающее применённое правило границ — какие коллабораторы реальны, какие застаблены и почему каждый выбор верен.
- Property-тест прогоняет ≥500 случаев с записанным seed, и ты можешь назвать, какая это форма property и один класс багов, что он защищает, который не защищали example-тесты.
- Короткое описание: каких выживших мутантов ты убил и как, каких (если были) ты триажировал как эквивалентных и почему, и одно решение по стабильности спеки с обоснованием.
- Встрой mutation testing в CI как diff-гейт (Stryker/PIT incremental), что валит билд, если mutation score изменённого файла падает, и покажи, как он ловит намеренно ослабленный assertion.
- Добавь oracle-property, что гоняет твой модуль против независимой эталонной реализации (медленной очевидной версии или второй библиотеки) над сгенерированным входом, и сшринкуй подсаженное расхождение до минимального контрпримера.
- Возьми один over-mock'нутый тест из существующей кодовой базы, перепиши его по правилу границ и покажи, что теперь он переживает сохраняющий поведение рефакторинг, но всё ещё падает на реальном внедрённом баге.
- Напиши одностраничную заметку для команды: когда TDD против spike на вашей кодовой базе, чек-лист правила границ для дублёров и как ты ограничиваешь mutation testing, чтобы оно окупалось без часов на прогон.
Это цикл, который ты будешь гонять на любом значимом модуле: проектируй под давлением test-first, чтобы интерфейс был верным до появления мест вызова, применяй правило границ, чтобы дублёры не пришпиливали тебя к структуре, добавь property там, где примеры слабее всего, и затем доказывай набор через mutation score, а не доверяя покрытию — читая выживших как список недостающих assertion. Решение по стабильности спеки держит тебя честным насчёт того, где TDD окупается, а где spike — верный инструмент. Сделай это раз на небольшом модуле — и продакшн-версия станет мышечной памятью.