「一份好的測試失敗報告,就像是鑑識現場的黑色盒子——它能讓工程師在不用重新本地重現(Local Replay)的情況下,3 分鐘內精準鎖定產品 Bug 還是環境問題。」
大家好,我是 Jane,一個每天在第一線處理跨平台(Web & App)自動化測試、跟 CI/CD Pipeline 失敗報告與 Issue 排查奮戰的自動化測試工程師(SDET)。
在上集 Day 23 與 Day 24 中,我們成功將測試改造成極速且抗併發衝突的平行測試體系。
但當自動化測試在 CI/CD 上高速運行、並在某個深夜跳出紅燈時,SDET 與開發團隊面臨的下一個考驗就是:
AssertionError: assert False,畫面截圖沒有、API Request/Response 沒抓、DOM State 也看不到……這到底要怎麼排查?」
如果每次 CI 測試失敗,工程師都必須親自把 Code checkout 到 Local 電腦、建環境、重跑一次腳本才能知道壞在哪裡,自動化測試帶來的效率就會大打折扣。
今天這篇文章,我們就來好好拆解:一份『可落地排查』的高品質測試失敗報告,至少要提供哪 9 大關鍵資訊?以及如何在 Python / pytest 中自動打包這些現場證據!
┌─────────────────────────┐
│ 失敗報告 9 大診斷要素 │
└────────────┬────────────┘
│
┌───────────────────────────┼───────────────────────────┐
▼ ▼ ▼
【視覺與軌跡現場】 【網路與系統狀態】 【環境與脈絡資訊】
1. 失敗當下的螢幕截圖 4. HTTP Request / Response 7. 測試案例名稱與 Intent
2. 操作錄影 (Video) 5. 前端/App Console Log 8. 執行環境 (Env / OS)
3. DOM 樹 snapshot / Trace 6. 後端 Trace ID / DB Log 9. 帶入的測試資料 (Data)
X-Trace-ID 附在報告上,方便後端工程師直接去 Datadog / Kibana 抓對應的後端 Log。我們利用 pytest 的 Hook 機制與 Playwright,打造自動在報告中附加「截圖、Trace 與 API Log」的通用 conftest.py:
Python
# tests/conftest.py
import os
import pytest
from playwright.sync_api import Page
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""
pytest 鉤子函式:在測試案例執行完畢後,自動攔截失敗狀態並附加證據
"""
outcome = yield
report = outcome.get_result()
# 僅針對測試執行階段 (call) 且結果為失敗 (failed) 的案例進行處置
if report.when == "call" and report.failed:
# 1. 獲取案例的 Playwright page 物件
page: Page = item.funcargs.get("page")
if page:
# 建立報告專屬目錄
os.makedirs("reports/artifacts", exist_ok=True)
test_name = item.name.replace("/", "_")
# A. 自動抓取當下螢幕截圖
screenshot_path = f"reports/artifacts/{test_name}.png"
page.screenshot(path=screenshot_path, full_page=True)
# B. 自動匯出 Playwright Trace (包含 DOM 樹與 Network 軌跡)
trace_path = f"reports/artifacts/{test_name}_trace.zip"
page.context.tracing.stop(path=trace_path)
# C. 將截圖與 Trace 連結注入至 HTML Report 中 (搭配 pytest-html)
if hasattr(report, "extra"):
import pytest_html
report.extra.append(pytest_html.extras.image(screenshot_path))
report.extra.append(pytest_html.extras.url(trace_path, name="下載 Playwright Trace 檔案"))
# 2. 獲取案例 Docstring 並作為「測試意圖」附加至報告
test_doc = item.function.__doc__ or "無文字說明"
print(f"\n[Failure Diagnostics] 案例: {item.name}")
print(f"[Test Intent]: {test_doc.strip()}")
conftest.py 中全域開啟 Trace 監聽為了讓失敗當下能順利匯出 trace.zip,我們需在 Browser Context 階段開啟Tracing:
Python
# tests/conftest.py (續)
@pytest.fixture(scope="function", autouse=True)
def auto_tracing(page: Page):
"""
自動為每個 Web UI 案例開啟 Playwright 軌跡錄製
"""
# 開啟追蹤:包含 Screenshots, DOM Snapshots, Network
page.context.tracing.start(screenshots=True, snapshots=True, sources=True)
yield
# 如果案例成功過關,直接關閉不存檔,節省 CI 磁碟空間
try:
page.context.tracing.stop()
except Exception:
pass
擁有了這份高資訊量的測試報告後,第一線 SDET 排查 CI 失敗的標準順序為:
┌─────────────────────────────────────────────────────────────┐
│ SDET 3 分鐘排查 S.O.P. │
├─────────────────────────────────────────────────────────────┤
│ Step 1. 看截圖/影片 ──▶ 判斷是 UI 跑版/跳 Toast 還是頁面空白 │
│ Step 2. 看 Network ──▶ 檢查背景 API 是否噴 40x / 500 錯誤 │
│ Step 3. 看 Trace/Log ──▶ 確認是否為 Locator 超時或元件未顯現 │
└─────────────────────────────────────────────────────────────┘
/api/v1/user/status 回傳了 500 Internal Error。複製報告中的 X-Trace-ID: trace_abc123,直接轉貼給後端工程師。trace.zip 拖入 trace.playwright.dev,精準查看 DOM 樹在第 3.2 秒時的元素狀態,確認是否為前端 DOM 改版造成。恭喜你!到今天 Day 25 為止,我們完成了整個系列的《第五部分:讓測試真正進入 CI/CD》!
我們解決了:
pytest-xdist 平行執行與極速優化從明天開始,我們將邁入整條自動化測試旅程的最精華、也最考驗資深價值的最終篇章:
《第六部分:資深測試工程師真正需要處理的問題》。
明天 Day 26,我們將一起來探討第一線 SDET 每天被問到的經典難題:
《 Day 26|如何判斷失敗是產品 bug 還是測試 bug?排查順序與邏輯 》。