測試報告:接進 ReportPortal,把結果變團隊看得到的儀表板
#ReportPortal #測試報告 #pytest #QA 實戰
你跑完一輪回歸,終端機顯示「23 passed, 2 failed」。然後呢?截圖貼 Slack?每次都要手動說明哪裡壞了?這不叫報告,叫廣播。真正的報告是讓開發、PM、測試主管,不用問你就能自己看到品質狀態,這就是 ReportPortal 在做的事。 我入職規劃整套自動化架構的時候,報表就是其中一塊。選 ReportPortal 的理由很單純:我要一個主管和團隊打開就看得懂的 dashboard,而不是一份只有我看得懂的 HTML 報告。 為什麼要 ReportPortal 終端機的輸出是一次性的;ReportPortal 把每次執行的結果存進儀表板: | 能力 | 說明 | | | | | 即時結果 | 測試跑完自動推送,不用人工上傳 | | 歷史趨勢 | 看到這條測試是「一直不穩」還是「這版才壞」 | | 失敗自動分類 | 同一個錯誤訊息的失敗自動歸群,一眼看出影響範圍 | | 附截圖與 log | 失敗當下的畫面和完整 log 直接在 RP 裡看,不用回去翻 CI | 這讓 QA 不必每次開完 CI 都要當翻譯,品質狀態直接攤在整個團隊面前。 pytest 接 RP 安裝套件: 在 pytest.ini 加入 RP 設定: rp endpoint:你的 ReportPortal 伺服器 URL。 rp project:RP 後台的專案名稱,要先建好。 rp launch:這次執行顯示的標題;可以在 CI 用環境變數帶入版本號。 rp launch attributes:附加標籤,方便後台篩選(環境、版本、branch)。 注意這份設定裡 沒有 API key 。pytest.ini 會被 commit 進版控,key 寫在這裡等於公開。我的做法是用環境變數 RP API KEY:本機放在 .env(加進 .gitignore),CI 上放在 GitHub 的 secrets。 還有幾個我實際在用、很少被提到的設定: 第一個最重要。報表系統是輔助,它掛掉的時候,測試應該照跑,不能反過來被報表拖垮。 跑測試時加 reportportal flag,結果就會即時推送: CI pipeline 裡通常長這樣: 失敗自動附截圖 RP 接了結果還不夠,失敗時的截圖才是讓開發馬上定位問題的關鍵。在 conftest.py 掛 pytest runtest makereport hook: pytest runtest makereport 在每個測試階段(setup / call / teardown)都會觸發;只攔 call 階段失敗,避免 setup 錯誤也截圖產生誤導。logger.error 搭配 extra["attachment"] 是 pytest reportportal 的附件格式:RP handler 會把這包資料上傳成附件,直接掛在失敗的 test item 下面。 如果測試同時需要附文字 log(例如 API response body),同樣透過 logger 傳: 怎麼讀 RP RP 最有價值的功能是 失敗分類 (Defect Type)。每個失敗預設進「To Investigate」,QA 看完之後要手動標記: | 分類 | 意義 | 後續動作 | | | | | | Product Bug | 產品功能壞了 | 開 bug ticket 給開發 | | Automation Bug | 測試程式本身的問題(selector 過期、斷言邏輯錯) | QA 修測試,不開 bug | | System Issue | 環境、裝置、網路問題 | 通知 DevOps / Infra,重跑確認 | 把同一批失敗分類完,RP 的 Launch 頁就會顯示圓餅圖,讓團隊一眼看出這次回歸「是品質問題還是環境問題」。這個判讀直接影響下一步是擋發布還是重跑。 RP 的歷史趨勢圖讓你看出某條測試是「三個 sprint 前就開始不穩」,而不是「這次才壞」。這種脈絡在事後檢討和排優先順序時很重要,靠終端機 log 根本回不去看。 手動分類撐不久,所以我讓它自動分 上面說「QA 看完之後要手動標記」。這在測試少的時候可行,但我一個人維護,每輪跑完都要一條一條標,很快就會變成「先不標好了」。不標的後果是 dashboard 上的失敗原因趨勢圖全部顯示 To Investigate,等於沒有這張圖。 所以我寫了一支腳本,接在 CI 跑完之後,自動做四件事:診斷失敗、把分類寫回 RP、算出這輪的裁決、發 Slack 通知。 分類的依據照這個順序判斷: 1. 失敗截圖做 OCR,看畫面上有沒有明顯的錯誤訊息 2. 錯誤訊息的關鍵字:timeout、WDA、StaleElement、AssertionError 各自指向不同分類 3. 測試名稱屬於哪個模組 4. RP 上既有的分類 5. 都判斷不了,就留在 To Investigate,交給人看 寫回 RP 的時候要帶 ignoreAnalyzer: true。不帶的話,RP 內建的機器學習自動分析會把你寫進去的分類蓋掉。這是我踩過才知道的。 用分類結果決定要不要擋發版 分類完之後,腳本會算出一個裁決,接進 CI: | 裁決 | 條件 | CI 的反應 | | | | | | 🟢 綠 | 通過率 ≥ 85%,且沒有 Product Bug | 通過 | | 🟡 黃 | 通過率 < 85%,但沒有 Product Bug | 通過,但發 Slack 警告 | | 🔴 紅 | 只要有任何一個 Product Bug | workflow 失敗,擋發版 | 這個設計的重點是: 通過率低不擋,有產品 bug 才擋。 通過率低通常是環境或腳本的問題,那是我的問題,不該拿來擋開發發版。但只要有一個失敗被判定為產品 bug,就代表 App 真的有東西壞了,這時候擋下來才有意義。這樣紅燈一亮,大家知道是真的要處理,而不是「又是自動化在鬧」。 讓 dashboard 可以篩 測試上的 marker,我在 conftest 裡自動轉成 RP 的屬性:功能模組、測試類型、平台、優先級、使用者身分。這樣在 RP 上就能用這些欄位篩選。 我的 dashboard 大概長這樣: | Widget | 篩選 | 用途 | | | | | | 通過率摘要 | 最近 10 次 | 整體趨勢 | | 失敗原因趨勢 | 全部 | 產品 bug、腳本問題、環境問題的比例 | | 最常失敗的測試 | 只看 p0 | 反覆失敗的核心測試 | | 執行時間統計 | 無 | 找出異常慢的測試 | | 整體統計 | 單一功能模組 | 某個模組的健康度 | 第二張只有在失敗都有分類的情況下才準。這也是為什麼自動分類那一步不能省。 我在 RP 上踩過的坑 log API 常常回 0 筆。 我們的 RP 環境裡,用 API 撈測試 log 經常什麼都拿不到。後來我寫報告時改用別的線索:每個測試上的中文描述、分類結果和測試名稱,一樣推得出失敗原因。 INTERRUPTED 不等於失敗。 狀態是 INTERRUPTED 的 launch,通常是 CI 超時或有人手動停掉,不是測試真的壞了。如果好幾個 launch 的結束時間一模一樣,多半是被一起砍掉的,寫報告時要標註清楚,不要算進失敗率。 預設截圖,不錄影。 錄影很慢、檔案很大,iOS 尤其明顯。我的框架預設只在失敗時自動截圖,需要錄影的測試再個別打開。 它是 flaky 追蹤的另一個視角。 上一篇的 flaky 追蹤器記的是本機單輪的即時資料,RP 上看得到跨很多輪的趨勢。兩個一起看,才知道某支測試是「這週才開始不穩」,還是「一直都不穩」。