Отчёт нужен не автоматизатору, а команде: разработчик должен за минуту понять, что сломалось, а менеджер увидеть, можно ли релизить. Тест без понятного отчёта о падении наполовину бесполезен.
JUnit XML: общий язык
Почти любой раннер умеет писать результаты в формате JUnit XML, и почти любой CI умеет его читать (вкладка Tests в GitLab, аннотации в GitHub через экшены, Jenkins).
<testsuite name="api" tests="3" failures="1" errors="0" skipped="1" time="2.31">
<testcase classname="tests.test_orders" name="test_create" time="0.41"/>
<testcase classname="tests.test_orders" name="test_pay" time="1.90">
<failure message="assert 500 == 200">...</failure>
</testcase>
<testcase classname="tests.test_orders" name="test_refund" time="0">
<skipped message="BUG-123"/>
</testcase>
</testsuite>
failure это не прошла проверка, error это неожиданное исключение (в pytest ещё и ошибка в фикстуре). Включение: pytest --junitxml=report.xml, в Playwright репортер junit.
Allure
Allure Report строит из результатов наглядный HTML-отчёт: дерево тестов, шаги, вложения, графики, история. Работает с pytest, JUnit, TestNG, Playwright, Jest и другими через адаптеры.
Как это устроено:
- Адаптер во время прогона пишет файлы в папку результатов:
*-result.jsonна каждый тест,*-container.jsonдля фикстур, вложения. - Командой
allure generate allure-results -o allure-report --cleanиз результатов собирается статический сайт. Для локального просмотраallure serve allure-results.
import allure
@allure.feature("Корзина")
@allure.severity(allure.severity_level.CRITICAL)
def test_apply_promo(cart_api, user):
with allure.step("Добавить товар"):
cart_api.add(user, sku="A1")
with allure.step("Применить промокод"):
r = cart_api.promo(user, "SALE10")
allure.attach(r.text, "ответ", allure.attachment_type.JSON)
assert r.json()["total"] == 900
Запуск: pytest --alluredir=allure-results.
Статусы Allure
- passed: прошёл.
- failed: не прошла проверка (AssertionError). Скорее всего баг продукта.
- broken: упал с другим исключением: таймаут, NullPointer, ошибка в фикстуре. Скорее всего проблема теста или окружения.
- skipped: пропущен.
- unknown: результат не удалось определить.
Разделение failed и broken экономит время: падения сразу делятся на «чинить продукт» и «чинить тесты». По умолчанию Allure так и называет категории: Product defects и Test defects.
Что делает отчёт полезным
- Шаги на языке сценария, а не «click», «click», «fill».
- Вложения при падении: скриншот, трейс или видео для UI, запрос и ответ для API, логи.
- Метки:
feature,story,severity,owner, ссылки на задачу и тест-кейс. - Свои категории в
categories.json: «Таймаут стенда», «502 от шлюза» по регулярке сообщения. Падения группируются по причинам, а не по тестам. environment.properties: стенд, версия сборки, браузер.
История и тренды
Allure показывает историю теста и графики трендов, если перед генерацией скопировать папку history из прошлого отчёта в новую папку результатов. В CI это делают шагом пайплайна. Тест, который в истории то зелёный, то красный, Allure помечает как нестабильный.
Уведомления и метрики
После прогона в чат команды уходит короткое сообщение: сколько прошло, сколько упало, ссылка на отчёт. Отдельно стоит следить за метриками: время прогона, доля флаки, число падений по категориям. Для централизованного хранения результатов и ручных кейсов есть TMS (например, Allure TestOps, TestIT), но это уже коммерческие системы.
Частые ошибки
- Отчёт без вложений: по падению в CI ничего не понятно.
- Шаги на уровне кликов, отчёт на тысячу строк.
- История не переносится, тренды пустые.
- Отчёт генерируется, но его никто не открывает: нет ссылки в уведомлении.
Что спросят на собесе
- Что такое JUnit XML и зачем он CI?
- Как подключить Allure к pytest или Playwright?
- Чем статус failed отличается от broken?
- Что вы прикладываете к отчёту при падении?
- Как получить в Allure историю и тренды?