iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
Vibe Coding

一個 Vibe Coding 專案從原型到有人在用系列 第 9

把修正拿掉,這個測試還是綠的

  • 分享至 

  • xImage
  •  

你叫 AI 修一個 bug。它改好了,還多寫了一個測試,專門守這個 bug。測試跑下去,綠的。

你大概會覺得穩了:以後誰改壞,這個測試會先叫。

可是有一種測試,把修正整個拿掉,它照樣是綠的。它從頭到尾沒碰到那個 bug。

6 月 1 日,一位老手送來修正,還附了測試

usage 是我做的開源小工具,放在 Mac 螢幕最上面那排選單列,顯示 Claude Code、Codex 這幾家 AI 工具的額度。點開圖示,每一家一張卡片,寫著額度用了百分之幾。

6 月 1 日下午,一位開發者在 GitHub 送來一個 PR(改好的程式,請你合併)。他不是第一次來,之前已經有 11 個 PR 合併進 usage,最近一個就在當天上午。他說在某種情況下,Codex 那張卡片會暫時消失;他改了 3 行程式,另外附了一個 37 行的測試。說明欄裡,格式檢查、型別檢查、跑全部測試、有改畫面的話實際打開看過,四個方框都打了勾。

Codex 的卡片,為什麼會自己消失

Codex 會把它跟伺服器之間的往來訊息,一筆一筆記進你電腦裡的一個資料庫檔。其中一種是額度紀錄,開頭寫著 "type":"codex.rate_limits",後面接著用了多少。usage 就是讀這一筆來畫卡片。

usage 找它的方法分兩步:先撈出最新的 50 筆「提到額度那串字」的紀錄,再從這 50 筆裡挑出真正的額度紀錄。下面把這 50 筆叫做 50 格。

他說問題出在「提到」。你叫 Codex 去查這個資料庫時,它下的查詢指令裡就有這串字,這個指令本身也被記了進去。這種被記下來的指令,下面叫它回音:它只是提到額度,不是額度。第二步會把它們挑掉,可是它們已經佔了 50 格裡的位子。他說回音一多,50 格全被佔滿,真正的那筆被擠到第 51 名以後,usage 找不到,Codex 那張卡片就暫時不見了。

他的修法是把第一步收緊:這串字要出現在紀錄的最開頭,才撈進來;夾在指令中間的,不撈。

他附的測試就照這個情境設計:做一個假資料庫,放一筆真的額度紀錄,再放 80 筆比它更新、中間提到那串字的雜訊。最後檢查 usage 還讀得到額度:5 小時內用了 40%、7 天內用了 6%。

Claude 把程式換回舊版,測試照樣過

我那時正在用 Claude Code 整理選單列的程式,看到這個 PR,先問 Claude 會不會跟剛整理的東西打架。Claude 查完說不會,兩邊改的是不同檔案。它接著問要不要順便審這個 PR,我回「好」。

十分鐘後 Claude 回報。它自己做了三筆紀錄,一筆真的額度、兩筆回音,拿新舊兩種撈法各撈一次:舊的三筆全撈,新的只撈真的那筆。所以修法是對的,全部測試也都過。然後補了一段:

我把程式換回舊版跑這個測試——它照樣綠。意思是這個測試證明不了 bug 被修掉,將來有人不小心改壞也擋不住(沒有防護力)。

修 bug 時附的測試,是用來證明「這次的修正有效」。如果把改的那個檔換回還沒修的版本,它一樣會過,那它證明的只有一件事:舊程式本來就過得了這個測試。

PR 附的那 80 筆雜訊,舊程式本來就不會撈

差別在一條反斜線。

舊程式第一步在找的,是 "type":"codex.rate_limits" 這串字。測試裡那 80 筆雜訊,是把查詢指令包在一段文字裡的寫法,寫成 \"type\":\"codex.rate_limits\",每個引號前面多了一條反斜線。對人來說是同一串字;對電腦來說,多了反斜線就是不同的字,舊程式比對不上。

所以這 80 筆雜訊,在舊程式裡根本進不了那 50 格。50 格裡只有真的那一筆,舊程式讀得到額度,測試當然綠。這個測試想重現「50 格被雜訊佔滿」,可是它放的雜訊,一筆都沒擠進去。

真實紀錄裡的回音有沒有反斜線,PR 沒附樣本。寫這篇時我想回頭查,我電腦上的 Codex 紀錄檔只剩 9 月 12 日一天,已經沒有這類紀錄,這個 bug 在真實紀錄裡多常發生,我查不回去。修正倒是沒壞處:有反斜線、沒反斜線兩種回音,新撈法都擋在外面,後面重跑的結果會看到。能確定的是:PR 附的那 80 筆,舊程式撈不到。

補一個會紅的測試

Claude 補了第二個測試,跟 PR 附的那個幾乎一樣:一筆真的額度紀錄、80 筆更新的雜訊。唯一的差別是,雜訊裡那串字不加反斜線,寫成舊程式撈得到的樣子。雜訊的開頭仍是別種紀錄,那串字夾在中間。

這次在舊程式上,80 筆雜訊都有那串字,全被撈進來,把 50 格塞滿,真的那筆被擠出去,usage 讀不到額度,測試失敗。換成修好的程式,只撈開頭就是那串字的紀錄,雜訊全被擋在外面,額度讀得到,測試通過。

https://ithelp.ithome.com.tw/upload/images/20260923/20183178s8jN4DoYHl.png

寫這篇時,我把兩個測試分別放在修正前、修正後的程式上跑,四種組合重跑了一遍:

                 舊程式(還沒修)   新程式(修好的)
PR 附的測試        通過               通過
Claude 補的測試    失敗:讀不到額度    通過

只有第二個測試,能分出修了跟沒修。

PR 在下午 5 點 32 分照樣合併了。Claude 審完時就說過,修法是對的,測試弱不影響功能。5 點 43 分,Claude 補的第二個測試也進了專案。

他打了四個勾,範本少問了一格

修法沒問題,該做的檢查他也都做了,可是一個守不住 bug 的測試,就這樣一路過關。回頭看他打勾的那張表。

那四格不是他自己列的,是 usage 的 PR 範本。5 月專案剛放上 GitHub 時我就寫好了,每個人送 PR,說明欄都會先帶出這四格。

前三項 Claude 審查時也跑過,確實都過。可是四格問的都是你做了什麼:跑了沒、過了沒、打開看了沒。沒有一格問你的測試證明了什麼:把你的修正拿掉,它會不會紅?

「拿掉修正會不會紅」,其實就是故意把程式弄壞,看測試抓不抓得到。這一步,同一天凌晨就有 AI 做過。我平常叫 AI 做事,會先寫一張工作單(交給 AI 的任務說明,Day 2 介紹過),其中一格是「做完怎麼算對」。Claude 那時派另一個 AI 寫一個測試:usage 裡有兩段程式各自加總同一份用量,測試檢查兩邊算出來一樣。那張工作單的這一格寫著:故意把其中一段算出來的數字乘以二,確認測試會失敗,驗完再改回來。接工作的 AI 照做了,回報兩邊從 350 對 350 變成 350 對 700,測試抓到了。

https://ithelp.ithome.com.tw/upload/images/20260923/20183178RCNExQ6IfH.png

AI 經手的工作走工作單,工作單裡可以寫這一步,那天凌晨那張就寫了。別人送來的 PR 走範本,範本裡沒有這一步。那天下午會抓到,是因為 Claude 審查時自己多做了,不是流程要求的。

你的測試,拿掉修正會不會紅

修 bug 的時候,叫 AI 做三件事,把輸出貼給你:

  1. 把修正暫時拿掉,測試留著。
  2. 跑這次新加的測試,它應該紅,而且紅的原因要是那個 bug,不是打錯字、少了檔案。
  3. 放回修正再跑,它應該綠。

你不用讀懂測試的程式碼,只要看它紅的時候說了什麼;看不懂,就叫 AI 翻成一句白話。

「紅的原因要是那個 bug」這半句最容易漏掉。我重跑時,Claude 補的測試在舊程式上紅,印出來的是 assert None is not None,字面上沒提到額度。翻成白話是:該讀到額度的地方,拿到的是空的。這正是那個 bug:usage 讀不到額度,卡片才會消失。如果它紅在別的地方,比如假紀錄建不起來,那也只是紅了,不代表碰到了 bug。

不管測試是誰寫的,都一樣要做:AI 寫的、別人送來的、你自己寫的。拿掉修正還是綠,這個測試就沒測到東西,要補一個會紅的,或把它重寫。

今天可以帶走的

工作單裡有一格「做完怎麼算對」,Day 4、Day 8 各在這格加了幾行。今天再加一行:

做完怎麼算對:
- (Day 4、Day 8 加的幾行)
- 修 bug 附的測試:先拿掉修正跑一次,要紅,而且紅在___(bug 發生時的樣子);放回修正再跑,要綠。兩次的輸出都貼在回報裡。拿掉修正還是綠,就補一個會紅的測試

如果你的專案收別人的 PR,把同一條檢查加進 PR 範本:

- [ ] 把這次的修正拿掉,新加的測試會失敗(貼上失敗訊息)

審別人的 PR 時,不管範本有沒有這一格,都先問一句:這個測試,換回舊程式會紅嗎?

今天這個測試,是審 PR 的時候抓到的。明天看另一道關:usage 每次推上 GitHub,都會自動跑一輪檢查。四個月裡它亮過 76 次紅燈,其中 54 次,是程式已經推上主線(main)之後才亮的。

參考資料

  • usage 專案:https://github.com/aqua5230/usage
  • 外部貢獻者的修正(PR #23):https://github.com/aqua5230/usage/pull/23
  • 補一個在舊程式上會失敗的測試(commit 794d2c1):https://github.com/aqua5230/usage/commit/794d2c1
  • usage 的 PR 範本(PR #23 送出時):https://github.com/aqua5230/usage/blob/e174d25/.github/pull_request_template.md

上一篇
測試全綠的 189 個版本,有個功能從第一版就打不開
系列文
一個 Vibe Coding 專案從原型到有人在用9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言