Три месяца назад на проекте с еженедельными релизами smoke-прогон стабильно показывал один flaky-тест из двенадцати. Команда привыкла - «это же smoke, один нестабильный тест погоды не делает». В пятницу вечером деплой ушёл в прод, flaky-тест снова упал, но gate пропустил сборку по правилу «pass if >90%». В субботу утром клиенты не смогли оплатить заказ. Сломанный платёжный эндпоинт прятался именно за тем тестом, который все привыкли игнорировать.
Проблема была не в smoke-наборе - он ловил дефект. Проблема была в обратной связи: результат прогона никто не разбирал, порог прохождения был мягким, а уведомления улетали в общий канал, где их не читали.
Smoke тестирование без выстроенной петли обратной связи - дымовой датчик с отключённой сиреной. Фиксирует проблему, но никто не реагирует. Ниже - как выстроить feedback loop от smoke-прогона до конкретного действия разработчика: с конфигами, метриками и антипаттернами из реальных пайплайнов.
Smoke test как CI/CD pipeline gate: не отчёт, а блокировка
Большинство русскоязычных материалов описывают smoke тестирование как «быструю проверку работоспособности перед углублённым тестированием». Это правда, но только половина. Согласно методологии Harness DevOps Academy, smoke-тесты в CI/CD - это gate, блокирующий продвижение сборки по пайплайну. Разница принципиальная: отчёт можно проигнорировать, gate - нет.Gate отвечает на один вопрос: «Сборка достаточно здорова, чтобы двигаться дальше?» Если ответ «нет» - пайплайн останавливается, сборка не попадает в следующее окружение, разработчик получает уведомление. Если «да» - запускаются более тяжёлые тесты: интеграционные, E2E, нагрузочные.
Быстрая проверка сборки через smoke ловит категории дефектов, которые unit-тесты не покрывают по определению:
- Сервис не стартует после деплоя (сломанный Dockerfile, пропущенная миграция)
- Роутинг возвращает 404/500 на критических эндпоинтах
- Переменные окружения не подтянулись - секреты, feature-флаги, connection strings
- Зависимости недоступны: база, кэш, внешний API
Где ставить smoke checkpoint в pipeline
На практике три точки, где smoke-прогон даёт максимальную отдачу:- После деплоя в staging - базовая точка. Ловит проблемы конфигурации, секретов, маршрутизации. Обязательна для любого проекта с CI/CD pipeline.
- Перед промоушеном в прод - go/no-go gate. Staging и prod всегда отличаются по инфраструктуре (даже если вам кажется, что нет - отличаются), и повторный smoke на pre-prod среде страхует от «на staging работало, на проде упало».
- На canary при progressive delivery - smoke на canary-подах до увеличения трафика. Актуально для Kubernetes-деплоев с Argo Rollouts или Flagger.
Минимальный набор smoke тестов: что включать в suite
Требования к окружению
Перед настройкой smoke-suite определите контекст:- CI-система: GitLab CI 16+ или GitHub Actions (примеры ниже для обоих; Jenkins 2.400+ - аналогичная логика через pipeline stages)
- Тестовый фреймворк: pytest 7+ с плагином pytest-timeout для API-проверок, либо Newman (CLI для Postman) для API-only проектов
- Сетевые условия: smoke-runner должен иметь сетевой доступ к staging-окружению (DNS-резолвинг, VPN-туннель если staging изолирован)
- Тестовые данные: фиксированный smoke-пользователь в staging-БД, создаваемый через seed-миграцию или setup-фикстуру
- Лимит времени: весь smoke-suite укладывается в 5 минут. Дольше - это уже не smoke, а мини-регрессия
Критический путь тестирования: что включать, что нет
Smoke-suite должен быть маленьким - 5–10 проверок. Больше - дольше feedback и меньше доверия к результатам. Я видел suite на 40 тестов, который выполнялся 12 минут. Через месяц его перестали ждать и начали скипать.| Проверка | Почему критично | Пример |
|---|---|---|
| Healthcheck-эндпоинт | Сервис жив и отвечает | GET /health возвращает 200 |
| Авторизация | Основной user flow не сломан | POST /auth/login возвращает 200 + token |
| Главный read-эндпоинт | Данные отдаются | GET /api/orders возвращает 200 + непустое тело |
| Главный write-эндпоинт | Запись работает | POST /api/orders возвращает 201 |
| Connectivity к БД | Миграции применились | Через healthcheck с DB-probe |
| Главная страница | Frontend собрался | GET / возвращает 200 + содержит <title> |
Не включать в smoke: тесты бизнес-логики (интеграционные), UI-тесты со сложными flow (E2E), нагрузочные проверки, визуальное регрессионное тестирование.
API-проверки предпочтительнее UI-тестов в smoke. API-assert стабильнее, быстрее и не зависит от рендеринга браузера. Сломана UI-кнопка, но API работает - дефект, но не smoke-level блокер.
Автоматизация smoke тестов: код и pipeline-конфиг
Smoke-тест на pytest для типичного REST API выглядит лаконично. Главное - детерминированные assert'ы и разумные таймауты:
Python:
# tests/smoke/test_smoke.py
import requests, pytest
BASE = "https://staging.example.com"
@pytest.mark.timeout(10)
def test_healthcheck():
r = requests.get(f"{BASE}/health")
assert r.status_code == 200
@pytest.mark.timeout(10)
def test_auth_flow():
r = requests.post(f"{BASE}/auth/login",
json={"email": "smoke@test.io", "password": "smokePass1!"})
assert r.status_code == 200
assert "token" in r.json()
Интеграция в GitLab CI как blocking gate:
YAML:
# .gitlab-ci.yml (фрагмент)
smoke_test:
stage: verify
script:
- pip install pytest requests pytest-timeout
- pytest tests/smoke/ -v --timeout=60 --junitxml=smoke.xml
artifacts:
reports:
junit: smoke.xml
rules:
- if: $CI_COMMIT_BRANCH == "main"
allow_failure: false # gate: блокирует pipeline при падении
allow_failure: false. Именно это превращает прогон из информационного отчёта в полноценный gate. Хоть один smoke-тест упал - pipeline останавливается, деплой в прод не произойдёт. В GitHub Actions аналог - отсутствие continue-on-error: true в step'е с тестами.Качество обратной связи при smoke тестировании: петля от падения до фикса
Smoke-gate заблокировал pipeline - и что дальше? Вот тут начинается зона, которую русскоязычные материалы обходят стороной. Gate без feedback loop бесполезен: pipeline красный, но никто не знает почему, кто чинит и насколько срочно.Четыре звена feedback loop
Звено 1 - Детекция. Smoke-тест упал, pipeline заблокирован. Автоматическое звено - работает, если настроен gate.Звено 2 - Нотификация. Конкретный человек (не «канал команды», а автор последнего коммита) получает сообщение с контекстом: какой тест упал, на каком окружении, ссылка на лог прогона, время падения для корреляции с деплоем. В GitLab CI переменная
$GITLAB_USER_LOGIN содержит автора пайплайна, в GitHub Actions - github.actor. Уведомление через Slack webhook или автоматический Jira-тикет с этими данными сокращает time-to-action с часов до минут.Звено 3 - Диагностика. Разработчик открывает отчёт (Allure, JUnit XML-артефакт, встроенный CI-дашборд) и видит: какой assert не прошёл, какой HTTP-код вернулся вместо ожидаемого, stacktrace если есть. Хороший smoke-тест в сообщении об ошибке содержит контекст:
Expected 200, got 503 on /auth/login after deploy abc123f. Плохой - AssertionError. Разница между «починил за 5 минут» и «копался час в логах».Звено 4 - Действие. Fix и повторный прогон. После исправления smoke автоматически перезапускается на свежем деплое. Цикл замкнулся.
Если любое звено разомкнуто - smoke не работает как инструмент качества. Самый частый разрыв - между звеньями 1 и 2: тест упал, но адресного уведомления нет, и failure висит в логе до следующего утреннего стендапа.
Метрики здоровья feedback loop
Недостаточно просто «запускать smoke и смотреть зелёный/красный». Метрики, за которыми стоит следить еженедельно:| Метрика | Что показывает | Целевое значение |
|---|---|---|
| Smoke pass rate | Процент успешных прогонов за неделю | >95% |
| Mean time to fix after smoke failure | Среднее время от падения до зелёного прогона | <30 минут для staging |
| Flaky test rate | Процент тестов с нестабильным результатом | <2% |
| Smoke suite duration | Время выполнения полного suite | <5 минут |
| False positive rate | Сколько раз smoke блокировал pipeline ложно | <1 из 20 прогонов |
Flaky rate - убийца доверия к smoke-gate. Тест мигает «красный/зелёный» без изменений кода - команда перестаёт верить gate и начинает его обходить. В pytest flaky-тесты можно изолировать через плагин pytest-rerunfailures с декоратором
@pytest.mark.flaky(reruns=2), но это костыль. Правильное решение - найти причину нестабильности (обычно таймаут, race condition или зависимость от внешнего сервиса) и устранить её в течение спринта. Не «когда-нибудь потом», а в этом спринте.Антипаттерны smoke-прогонов: что убивает обратную связь
За два года настройки smoke-gate на проектах разного масштаба - от стартапа до enterprise с 20+ микросервисами - я собрал устойчивый список антипаттернов. Каждый из них встречался не раз и не два.«Мягкий gate».
allow_failure: true в GitLab CI или continue-on-error: true в GitHub Actions. Smoke падает, pipeline продолжается, результат теряется в логах. Заявлено gate - реально декорация. Это самый коварный антипаттерн: формально smoke есть, по факту его нет.«Уведомление в общий канал». Smoke упал - сообщение улетает в #dev-general, где 40 человек и 200 сообщений в день. Никто не видит, никто не реагирует. Уведомление должно быть адресным: автору коммита или on-call инженеру.
«Smoke на 50 тестов». Suite разросся, выполняется 15 минут, половина тестов проверяют бизнес-логику. Это уже не smoke, а мини-регрессия. Результат: медленный feedback, частые ложные падения, команда теряет терпение и отключает gate. Видел такое дважды - оба раза заканчивалось одинаково:
allow_failure: true и «потом починим».«Нет тестовых данных». Smoke проверяет авторизацию, но тестовый пользователь удалён из staging-базы после последней миграции. Тест падает не из-за дефекта, а из-за инфраструктуры. Решение: smoke-данные фикстурятся в самом тесте (setup/teardown) или хранятся как seed в миграциях staging-окружения.
«Smoke только на staging». Команда запускает проверки только после деплоя в staging, но не перед промоушеном в прод. Staging и prod имеют разные DNS, секреты, version tags балансировщика - smoke на staging не гарантирует, что prod будет работать.
Таблица решений: когда блокировать, когда предупреждать
| Ситуация | Действие | Обоснование |
|---|---|---|
| Healthcheck возвращает 5xx | Блокировать pipeline | Сервис мёртв, тестировать дальше нечего |
| Авторизация сломана | Блокировать pipeline | Основной user flow недоступен |
| Один не-критический тест flaky | Предупредить, не блокировать | Но завести задачу на стабилизацию в текущем спринте |
| Время smoke >5 минут | Предупредить, провести ревизию suite | Suite вырос - нужна чистка |
| Все тесты зелёные, но latency >3 сек | Предупредить (performance smoke) | Не блокер, но сигнал деградации |
Разделение на «блокирует» и «предупреждает» реализуется через два отдельных job'а в CI: один с
allow_failure: false для критических проверок (healthcheck, auth), другой с allow_failure: true для advisory-проверок (latency, второстепенные эндпоинты). В pytest - через маркеры @pytest.mark.critical и @pytest.mark.advisory с фильтрацией через -m flag.E2E тестирование и smoke only: где проходит граница
Часто спрашивают: если smoke-тесты проверяют задеплоенную систему end-to-end (отправляют HTTP-запросы, получают ответы) - это E2E тестирование или нет?Формально - да, smoke-тесты работают с системой как чёрный ящик, проверяя её снаружи. Но между smoke и полноценным E2E - принципиальная разница в scope и intent:
- Smoke проверяет: «система жива, критические пути отвечают». 5–10 проверок, 2–3 минуты. Запускается на каждый коммит в main.
- E2E проверяет: «пользовательские сценарии работают от начала до конца». 50–200 сценариев через Playwright или Cypress, 30–90 минут. Запускается по расписанию или перед релизом.
Стратегия «smoke only» работает как gate, но не как единственный уровень автоматизации. Smoke закрывает вопрос «деплой не сломан?», E2E закрывает вопрос «фичи работают корректно?». В зрелом pipeline оба уровня дополняют друг друга: smoke блокирует очевидно сломанную сборку за 3 минуты, а E2E через 40 минут находит тонкие регрессии в бизнес-логике.
Восемь из десяти проблем с smoke-тестированием, которые я разбирал на проектах, были не проблемами тестов. Тесты работали. Проблема была в том, что результаты прогона уходили в пустоту - в нечитаемый лог, в общий канал, в отчёт, который открывали раз в спринт.
Smoke без обратной связи - мониторинг, на который никто не смотрит. Создаёт иллюзию контроля, но не порождает действия.
Мой подход: smoke-gate жёсткий по умолчанию, уведомление - адресное автору коммита, flaky-тесты устраняются в течение спринта, а не копятся месяцами. Команды, которые относятся к smoke как к формальности - «ну что-то там бегает в pipeline» - рано или поздно получают пятничный инцидент из начала этой статьи. Те, кто выстроил петлю обратной связи с метриками и адресными нотификациями, ловят дефекты на staging за минуты - до того как они стоят денег.
Разница не в инструментах и не в количестве тестов. Разница в том, замыкается ли цикл «упало → увидели → починили → перепроверили». Если цикл разомкнут на любом звене - smoke бесполезен, сколько бы тестов ни было написано. Проверьте свой pipeline прямо сейчас: уроните один smoke-тест намеренно и засеките, через сколько минут конкретный разработчик узнает об этом. Если ответ «никогда» или «на утреннем стендапе» - у вас та же проблема.