iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
佛心分享-IT 人職涯歷練

測試之外的那一半:一個 QA 回頭帶當年的自己系列 第 23 篇

那個測試只在連續跑的時候壞,單獨跑永遠正常

  • 分享至 

  • xImage
  •  

本文是「測試之外的那一半:一個 QA 回頭帶當年的自己」系列第 23 篇。

實習的時候,我的腳本有幾個步驟三不五時會失敗。

我的處理方式是加 retry。失敗了就重跑一次,第二次通常會過,報表就綠了。

那招有效。而且因為它有效,我用了很久才發現我在做什麼。

我在把證據丟掉

一個測試偶爾失敗,這件事本身是資訊。

它在說:在某些條件下,這個功能的行為跟平常不一樣。

加 retry 之後,那句話就消失了。報表變綠,沒有人會再去問那個「某些條件」是什麼。

我不是在修問題,我是在把問題的訊號關掉。

一個我追到底的案例

後來遇到一個不肯讓我蒙混過去的。

某個測試步驟,在連續跑一整輪的時候三不五時失敗一次,訊息是找不到頁面元素。但我單獨跑那一步,永遠重現不出來。

第一個線索是:App 的行程還活著。不是 crash。

第二個線索在截圖裡。失敗當下的畫面不是 App 本身,是一個客服性質的網頁,整個蓋住了畫面。

所以測試不是找不到元素。元素在那裡,只是被一層它不預期的東西蓋住了。這就是 Day 22 那張分層表最下面的覆蓋層。

但真正的問題是「為什麼只在連跑時發生」

找到覆蓋物還不夠。我想知道它從哪裡來——是 App 自己彈的,還是我的測試不小心點到了什麼。

這兩個答案的後果完全不同。如果是測試誤點,那是我的腳本要修。如果是 App 自己彈的,那是產品行為,而且可能會發生在真實使用者身上。

我用 logcat 追那個畫面的來源,查出發起它的 intent 屬於哪個 uid。結果是 App 自己。

再往下挖,原因很樸素:那是一個行銷推播,投放條件是「使用者累積走過幾個頁面」。

自動化跑得比人快太多,連續跑一輪就把那個累積條件湊滿了,於是推播在半路 fire 出來。單獨跑一個步驟,湊不滿,自然永遠不重現。

「單獨跑不重現、連續跑才壞」本身就是線索。 它指向累積狀態,不指向隨機。

如果我當初加了 retry

那個推播會繼續在真實使用者的某個頁面彈出來,蓋住畫面。

它不會變成 bug 單,因為唯一固定撞到它的是我的測試,而我的測試已經被我教會了「撞到就重跑」。

這是 retry 最貴的地方。它不只是掩蓋一個測試的失敗,它讓一個產品行為永遠不會被任何人注意到。

另一種 flaky,處理方式不一樣

不是所有 flaky 都藏著產品問題。

有一種很常見的:畫面用現代的宣告式 UI 框架寫,元件會在資料更新時重新產生。你抓到元素的那一刻它還在,等你要點的時候它已經被換掉了,於是報 stale element。

這種的正解是重抓一次,不是加 sleep。

sleep 是猜——猜多久夠。猜短了還是壞,猜長了每一輪都在浪費時間,而且它會讓你永遠不知道真正需要等多久。重抓是針對成因:元素被換掉了,那就重新拿一次。

兩個案例的處理方式不同,但判準是同一個:先弄清楚它為什麼壞,再決定怎麼處理。

那 retry 到底能不能用

能。但它是結論,不是反射動作。

我現在的順序是:先問「它在什麼條件下才發生」,追到成因,然後判斷那個成因能不能容忍。能容忍的才加 retry,而且要在註解裡寫清楚我容忍的是什麼。

寫下來這件事跟 Day 10 那三行關單紀錄是一樣的道理。沒有那行註解,半年後接手的人會看到一個 retry,然後完全不知道它在擋什麼——他只會知道拿掉它測試就會壞。

帶走的一樣東西

測試偶爾失敗的時候,先別加 retry。先問一句:

它在什麼條件下才發生?

特別注意這三種條件,它們都指向累積狀態而不是隨機:

只有連續跑才壞、只有跑到後半段才壞、只有在某台機器上才壞。

追到成因之後再決定怎麼處理。真的要加 retry,就在旁邊寫一行「我容忍的是什麼」。

明天聊:同一份腳本,我筆電跑得動,共用機器跑不動。


延伸閱讀


上一篇
「App 壞了」是結論,不是觀察
系列文
測試之外的那一半:一個 QA 回頭帶當年的自己 共 23 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言