Тестирование
Что покрывать тестами, какими именно и где они лежат. Документ общий для фронта и бэка: слои разные, принцип один — тест проверяет наблюдаемое поведение через границу, а не внутреннее устройство.
6 минутЧто покрывать тестами, какими именно и где они лежат. Документ общий для фронта и бэка: слои разные, принцип один — тест проверяет наблюдаемое поведение через границу, а не внутреннее устройство.
Место в цепочке
| Откуда пришли | Этот документ | Куда ведёт |
|---|---|---|
| код разложен по слоям (BMFP / BMBP) | что и как покрывать, где хранить тесты | сборка релиза, где прогон тестов — условие выпуска |
Что этот документ оставляет следующему: набор проверок, который запускается одной командой и даёт однозначный ответ «можно выпускать». Тесты, которые надо запускать вручную и толковать глазами, эту роль не выполняют.
Что проверять на каждом слое
Границы слоёв задают, что считать поведением, а что — устройством.
Бэкенд:
| Слой | Что проверяется | Что подменяется |
|---|---|---|
core (логика, сценарии) | правила предметной области: расчёты, переходы состояний, отказы | хранилище и внешние клиенты |
api | контракт: коды ответов, форма конверта, проверка входных данных, права | сценарии слоя core |
infrastructure | запросы к хранилищу и разбор ответов внешних систем | сама внешняя система |
| сквозной | путь запроса целиком на поднятых зависимостях | ничего |
Фронт:
| Слой | Что проверяется | Что подменяется |
|---|---|---|
domain (состояния, сервисы) | расчёты, правила формы, последовательность вызовов | клиенты infrastructure |
infrastructure/clients | разбор конверта, проверка ответа схемой, обработка ошибки | сеть |
boundary | что видит и делает пользователь: отображение, ввод, реакция на ошибку | сервисы domain |
Логика проверяется там, где она живёт. Если правило можно проверить только через интерфейс, оно оказалось не в том слое.
Виды тестов и их доля
| Вид | Что даёт | Чего стоит |
|---|---|---|
| на слой (изолированные) | быстрый и точный ответ, где сломалось | не ловит ошибки на стыках |
| на связку (несколько слоёв, настоящее хранилище) | ловит стыки: схема, запросы, транзакции | медленнее, нужна среда |
| сквозной | подтверждает, что путь работает целиком | самый медленный и хрупкий |
Основной объём — изолированные и на связку. Сквозных немного: они покрывают главные сценарии (вход, ключевая операция, оплата), а не каждую ветку.
Соотношение выводится из цели: набор должен отрабатывать достаточно быстро, чтобы его запускали перед каждым изменением. Набор, который идёт полчаса, перестают запускать.
Правила
Тест проверяет поведение, а не устройство. Обращение — через публичную границу единицы. Тест, знающий о приватных полях и порядке внутренних вызовов, ломается при любом переписывании, которое ничего не изменило снаружи.
Тест независим. Порядок запуска не влияет на результат: каждый готовит своё состояние и убирает за собой. Тесты, работающие только в определённом порядке, скрывают ошибки и мешают параллельному запуску.
Отказ теста называет причину. Из сообщения должно быть понятно, что сломалось, без чтения кода
теста. Проверка assert result менее полезна, чем проверка конкретного ожидаемого значения.
Один тест — одно утверждение о поведении. Не «проверить весь сценарий заказа», а «отказ при недостатке средств не создаёт заказ».
Ошибка сначала воспроизводится тестом. Тест, падающий до исправления и проходящий после, — это одновременно доказательство исправления и защита от повторения.
Внешние системы подменяются на своей границе. Подменяется клиент, а не сетевой вызов внутри него: тогда тест не зависит от того, какой библиотекой сделан запрос.
Данные для тестов
Состояние готовится самим тестом, а не общей базой, наполненной заранее: общий набор данных со временем становится непонятным, и никто не знает, что сломается при его правке.
Значения — осмысленные для проверки: если тест про отказ при недостатке средств, в нём видно, что средств не хватает. Случайные данные скрывают причину отказа.
Для тестов на связку хранилище поднимается на время прогона и удаляется после. Общая база на всех разработчиков даёт зависимость от порядка и от чужих данных.
Что проверять обязательно
Помимо основного пути:
- границы входных данных: пусто, максимум, неверный тип, лишние поля;
- отказы соседей: недоступное хранилище, таймаут внешней системы, ошибка очереди;
- права: чужой ресурс не отдаётся и не изменяется — проверка на каждой ручке, а не на одной;
- повторный вызов операций, которые обязаны быть идемпотентными;
- форма ответа: конверт и коды ошибок — по ним ветвится фронт.
Проверка прав в тестах особенно важна: ошибка здесь не проявляется в обычной работе и обнаруживается только тем, кто ищет её намеренно.
Где лежат тесты
Рядом с кодом, который проверяют, — так они видны при чтении и не забываются при переносе. Сквозные тесты, требующие поднятой среды, — отдельным каталогом на уровне продукта.
Тесты переиспользуемой единицы принадлежат ей: потребитель не обязан проверять её работу.
Запуск
Один прогон одной командой, без ручных предварительных шагов. Результат — код возврата: ноль или нет. Прогон входит в сборку релиза; сборка при непрошедших тестах не выпускается.
<команда прогона> # ожидается: все тесты прошли, код возврата 0
echo $? # ожидается: 0Типичные отказы
| Признак | Причина | Что делать |
|---|---|---|
| тесты падают через раз | зависимость от порядка или общего состояния | каждый тест готовит и убирает своё |
| после безобидного переписывания упало полсотни тестов | тесты знают внутреннее устройство | проверять через публичную границу |
| набор перестали запускать | идёт слишком долго | перенести объём с сквозных на изолированные |
| тесты зелёные, на рабочей системе ошибка | не покрыты стыки — схема, запросы, права | добавить тесты на связку с настоящим хранилищем |
| непонятно, что сломалось | проверка без конкретного ожидаемого значения | сравнивать с ожидаемым, а не проверять истинность |
| ошибка вернулась после исправления | не был написан воспроизводящий тест | сначала тест, потом исправление |
Откройте исходник документа по ссылке «Предложить правку» — там же видно, что и когда в нём менялось.