本文是「測試之外的那一半:一個 QA 回頭帶當年的自己」系列第 24 篇。
腳本搬上共用的執行機之後,卡在登入頁。
我筆電上跑得完。兩邊的腳本一字不差,我 diff 過。
我第一個念頭是「一定是我哪裡寫壞了」。
這是我這幾年改掉最有用的一個反射。
本地能跑、CI 不能,最常見的原因是環境差異,不是腳本錯。理由很簡單:腳本是同一份,它沒有變。變的是它跑在哪裡。
所以順序應該反過來。先問「這台跟我本機哪裡不一樣」,再問「我腳本哪裡錯」。
登入頁是一個網頁,嵌在 App 裡。問題出在它被裝進哪一種瀏覽器容器。
我的筆電上沒有裝完整的 Chrome。所以 App 找不到外部瀏覽器分頁的提供者,退回去用內嵌的 WebView——那跟 App 在同一個行程裡,測試工具注入得進去,元素也抓得到。
共用機器上有完整的 Chrome。所以 App 走了外部瀏覽器分頁。那是另一個行程,注入被擋掉,定位也對不上。
同一份腳本、同一個 App、同一個帳號。差別只在一台機器裝了 Chrome。
解法也很細:用 root 權限只停用 Chrome 提供外部分頁的那個元件(CustomTabsConnectionService),保留 WebView 引擎。不能整個停掉 Chrome,那會把 WebView 一起殺掉,App 連內嵌網頁都開不了。
那次之後我開始收集這類差異。它們長得都不一樣,但形狀一樣。
adb 連不到高編號的模擬器。 埠號是 5555 + index × 2,超過預設掃描範圍的要手動 connect。
共用機沒有我慣用的套件管理工具。 本機的腳本假設它存在,換成用 sys.executable 就通了。
排程起不了模擬器。 CI 服務跑在沒有桌面的工作階段裡,需要 GUI 的模擬器起不來。這個最花時間,因為錯誤訊息完全沒有提到「沒有桌面」這件事。
最後那個是重點:環境差異的錯誤訊息,幾乎不會指向真正的原因。 它只會告訴你「失敗了」。
我在自己的筆電上除錯,可以直接看畫面。在 CI 上不行——它跑完才告訴你結果,而那時候現場已經沒了。
所以後來我做了兩件事,而且我認為這是跑 CI 的最低門檻:
log 即時串流出來,不要等跑完才收。跑到一半掛掉的時候,你至少知道它掛在哪一步。
失敗的時候自動 dump:截圖、畫面元素結構、當前的 activity。這三樣東西合起來,通常一眼就能判斷是元素沒出現、被蓋住了、還是根本在別的頁面。
沒有這兩樣,每次除錯都是瞎子摸象,而且每摸一次要等一整輪跑完。
遇到「本地能跑、CI 不能」,我現在照這個順序:
前五步都在問「哪裡不一樣」。第六步才問「我哪裡錯」。
我實習的時候是直接從第六步開始的,然後在那裡待很久。
Day 22 那張診斷分層裡,環境層是最容易被錯怪的一層——因為它壞起來的樣子,跟產品壞掉長得一模一樣。
黑畫面可能是模擬器的 GPU,不是 App。登入卡住可能是機器裝了 Chrome,不是登入功能。
而這兩種誤判的共同代價是:你會送 RD 去查一個沒有壞的東西,來回幾次之後那張單被標成無法重現,然後大家都浪費了時間,也讓RD對你少了點信任,職場最怕的就是失去別人信任。
本地能跑、CI 不能的時候,先別改腳本。
拿一張紙,寫下這句話的答案:這兩台機器,我知道哪些地方不一樣?
寫不出三項以上,代表你對執行環境的了解不夠,而那個不夠正是問題的來源。
還有:在你動手查之前,先確認 CI 失敗的時候會自動留下截圖和畫面結構。沒有這個,你接下來每一次猜測都要等一整輪才能驗證。
明天聊:AI 在我的測試流程裡實際接手了哪幾塊,哪幾塊我還不敢交。