iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
Claude AI

Claude × Playwright:30 天打造你的 Agentic SDET 同事系列 第 14

Day 14|第一次寫 Bug Report:從發現到可重現報告

  • 分享至 

  • xImage
  •  

前言

昨天把異常分了類,七類裡只有 product-bug 有資格往下。今天處理往下的第一步:把它寫成一份報告。

素材是 2026-07-30 那批實際產出的 18 份 Bug Report(output/reports/issues/)。底下第三節那兩筆缺陷是比對報告與檔案系統查出來的,不是設想的。

為什麼這一天不是教你填範本

「寫 Bug Report」聽起來是全書最沒有技術含量的一天。範本網路上都有,欄位就那幾個。

但範本不是這一天的問題。真正的問題是:交給 agent 寫,結果會不會比人寫的差?

答案有點意外。人寫的報告最常見的死法不是寫太少,是寫太懂

CartComponentlineTotal 沒綁到 currency pipe。

寫的當下很有價值,工程師看一眼就知道去哪改。三個月後那個檔案被重構掉,這張單就沒人看得懂了 —— 而且更糟的是,你也不確定它到底修好了沒有,因為報告描述的是一個已經不存在的東西。

agent 沒有這個選項。它從外部操作產品、讀不到程式碼,只能用使用者語言描述:

購物車每一列的 Total 欄一律顯示 $00.00,但頁尾總計是對的。

這句話跨得過任何重構。這是限制變成優點的例子:看不到實作,所以只能寫行為,而行為才是那個要被修好的東西。

但要立刻補上代價。它也不知道自己在報什麼,沒有「這件事我大概記得」的直覺,也不會在寫到一半覺得哪裡怪怪的。所以四要件裡「每一條宣稱都要指得到證據檔」不是形式主義,那是唯一能查的東西。

驗收標準四條:可重現、有證據、耐久、精簡。

下一節就查給你看,查出來的結果不太好看。

實驗:同一批 18 份報告,查出兩筆缺陷

同一天、同一套範本、同一輪產出的 18 份報告,品質應該一致才對。

第一筆:證據引用有兩種寫法。

報告 證據怎麼寫
cart 那份 output/evidence/20260730-toolshop-oncall-bughunting/05-cart-totals.png(完整路徑)
contact 那份 A4-contact-subject-dropdown-errors.png只有檔名

購物車頁的金額,列小計與表尾總計對不起來

05-cart-totals.png,就是左欄那條完整路徑指到的檔案。

兩種都「附了證據」,但只有一種,接手的人不用問就找得到。這條直接接 Day 11 的交棒標準:你腦子裡記得那個檔案在哪,接手的人沒有。

還有更糟的一筆。broken.png 那份報告宣稱「畫面上可見兩處破圖」,證據指向 08-search-no-results.png檔案是對的,檔名跟宣稱對不上 —— 那張圖是搜尋無結果的截圖,破圖只是剛好也在畫面裡。打開的人會先愣三秒,然後開始懷疑這份報告其他地方是不是也這樣。

搜尋無結果的頁面,畫面上同時有兩處破圖

報告寫「畫面上可見兩處破圖」,附的是這張。破圖確實在裡面,但檔名說的是搜尋無結果。證據對得上,指路的字條對不上。

所以證據不是「有附」就好,是指得到、打開來看得懂

第二筆比較嚴重:自報的欄位是錯的。

20260730-toolshop-bughunt-gaps/candidates.yaml 裡的 C-NEW-002(就是 contact 下拉那筆)寫著:

evidence: []

同一份檔案的摘要也寫 evidence_exists: false

但那張截圖 A4-contact-subject-dropdown-errors.png 就躺在同一個資料夾裡。

Contact 頁的 Subject 下拉,選項裡出現錯誤字串

紀錄寫 evidence: []evidence_exists: false,而這張就在那個資料夾裡。自報欄位跟檔案系統,只有一個能信。

證據存在,只是沒被登記進紀錄。後來的報告引用得到它,是因為寫報告的那一輪自己去翻了資料夾,不是因為紀錄裡有。

教訓寫成一句:驗收要對著檔案系統勾,不能信自報欄位。

一支掃資料夾比對引用的小腳本就能擋掉這種事。誠實講,那支腳本現在還不存在 —— 而且這是連續第二天指向同一個缺口(Day 11 也提過,scripts/check-manifest.sh 檢查的是 plugin 的 skill 清單,不是證據包)。該寫了。

「可重現」的門檻在哪

看三份實測報告,它們的步數是 6、4、2。

對照一:從乾淨狀態開始(cart 那份,6 步)

第 1 步寫的是:

清空購物車(sessionStorage.removeItem('cart') 後重新整理)

這一步才是整份報告能不能重現的關鍵。沒有它,別人購物車裡有別的東西,看到的數字跟你寫的對不上,然後他會回你一句「重現不出來」。

第 6 步「把第一列數量改成 5,離開該欄位,再讀一次兩處金額」不是必要的重現路徑,而是證明它會重算頁尾卻不重算列小計,把缺陷的形狀鎖死。

對照二:被動現象也要寫怎麼看到(broken.png 那份,4 步)

那 4 步全是「打開 devtools」「記錄 4xx」「觀察某個位置」,沒有一步是操作

因為它是每一頁都有的被動現象,沒有什麼動作可以做。重現步驟不一定要有動作,但一定要有「怎麼看見」。

對照三:兩步也夠(contact 那份)

  1. #/contact
  2. 展開 Subject 下拉,讀所有選項

步數不是標準。標準是別人照著走,會不會到同一個畫面。

最後一條鐵則:湊不齊就不寫。 不要寫個「偶爾會發生」交差 —— 那一筆該回到 Day 12 的 flakyneeds-investigation,不該長成一份報告。

要誠實講一件事:這三份是「不同但都合格」的對照,這 18 份裡沒有一份真的重現不出來的反例,因為它們是同一套範本產的,品質接近。想拿反例教學,得去撿一份舊的,或自己寫一份壞的。

報告裡不該出現的東西

不寫檔案路徑與行號。 理由第一節講過了,這裡只重申成規則。

不寫「我猜是後端沒驗證」。 猜測放不進報告。能寫的是排除過程 —— cart 那份寫的是:

底層資料正確,購物車儲存的 total 欄位算得出正確金額,問題在列金額的呈現。

那是有證據的,不是猜的。差別在於前者你查得到依據,後者只是一個聽起來合理的說法。

還有一件事要對讀者誠實。實測那 18 份報告裡已經有「信心 high(0.70:2 情境重現 +0.15、oracle 內部一致性 +0.25⋯)」跟「獨立盲驗 V-C-01.yaml = confirmed」這種欄位。

那兩樣是第四週才長出來的東西,今天的示範不該有。今天的「分類與信心」段只寫兩行:product-bug,加一句判類依據(昨天的 basis 直接搬過來)。信心分數怎麼算、誰來盲驗,都還沒發生。

有些報告本身就是危險品

這一節不長,但不能不寫,因為讀者一定會遇到。

素材是 SQL injection 繞過登入那份。重現步驟寫得非常具體:email 欄填 admin@…' -- 注意尾端有一個空白,結果是拿到 role=admin 的有效 JWT。

重現步驟寫清楚,這份報告就是一份攻擊教學。但寫不清楚,它就修不掉。

那份報告的處理方式是四件事一起做:

  1. labels 加上 security
  2. 開頭獨立一個警告框,寫明這是「在刻意植入漏洞的 demo build 上、經授權的非破壞性存在性驗證」
  3. 寫明做到哪裡就停:取得 token 後立即登出,未進入 admin、未列舉或修改任何資料
  4. 預期行為那段寫成修法方向(參數化查詢),不只寫「應該擋下來」

第 3 點是最容易漏的。它同時保護兩件事:讀者知道這份報告的邊界在哪,而你也留下了自己沒有越界的紀錄。

今天只講「怎麼報」。該不該讓它自己去打 SQLi、報告要不要自動公開,那是治理問題,第四週處理。

標記這是 AI 寫的

每一則報告與留言開頭固定一行:

> *This was generated by AI during triage.*

理由不是免責,是讀的人有權知道這份沒有人逐字讀過

前面那兩筆查出來的缺陷正好證明為什麼需要這一行:證據引用只寫檔名、evidence: [] 卻有檔案,這兩種錯誤人不太會犯,但它會,而且犯得很自然、格式很整齊。

這條跟 Day 4 手冊裡那句同源:產物是不是真的產出來了,跟它說不說得出口,是兩回事。

小結

報告寫行為不寫實作,因為行為跨得過重構;四要件是可重現、有證據、耐久、精簡,而「可重現」的標準不是步數,是別人照著走會不會到同一個畫面。證據要給指得到的完整路徑,而且驗收時對著檔案系統勾,不能信自報欄位。安全類的報告要把邊界一起寫進去,AI 寫的要標記。寫完到此為止:一份寫好的報告,還沒開單。

              一筆 product-bug(Day 13 判完的)
                            │
                            ▼
                  ┌─── 四要件 ───┐
                  │              │
      可重現 ◄────┤              ├────► 有證據
   別人照著走      │              │    完整路徑,不是檔名
   會到同一個畫面  │              │    對著檔案系統勾
   (步數不是標準)│              │    不信自報欄位
                  │              │
        耐久 ◄────┤              ├────► 精簡
   寫行為不寫實作  └──────────────┘    不寫猜測
   跨得過重構                          寫排除過程
                            │
                            ▼
              安全類另加:labels、警告框、
              做到哪裡就停、修法方向
                            │
                            ▼
              開頭標記:this was generated by AI
                            │
                            ▼
                    一份寫好的報告
                            │
                            ▼
                   還不能開單,缺三關
        開單資格|去重與指紋|信心分數(都在第四週)

第二週學會的五件事收成一張表:

這一天在解決什麼
Day 9、10 看得到 —— 挑對證據來源,追到 network
Day 8、11 留得下 —— 證據包要能交棒
Day 12 說得清楚 —— Pass/Fail 之外還有四種
Day 13 算得清楚是誰的鍋 —— 七類裡只有一類能往下
Day 14 寫得出別人能重現的報告

五件事有個共同點:全部都在處理「你憑什麼相信它」。

寫行為不寫實作,CartComponent.lineTotal 三個月後就沒人看得懂,「列小計顯示 $00.00 但頁尾正確」跨得過任何重構。證據要指得到、打開來看得懂,只寫檔名的那份跟宣稱對不上的那份,都會讓接手的人先愣三秒。而自報欄位不能信,evidence: [] 旁邊就躺著那張截圖,驗收要對著檔案系統勾。

下一步

第二週從頭到尾,我都在給它逐步指令:去哪一頁、按哪個按鈕、導哪份 log、寫哪份檔案。

它照做得很好,但它一次也沒有自己決定要看什麼。

第三週換一種帶法:不給步驟,給目標、範圍與界線,剩下的路它自己走。


參考資料

  1. Simon Tatham — How to Report Bugs Effectively - 1999 年的文章,「可重現」這條標準的原型
  2. Mozilla — Bug writing guidelines - 四要件的業界版本
  3. Claude Code Docs — Extend Claude with skills - 報告範本怎麼固定成 skill 的一部分
  4. OWASP — SQL Injection - 危險品那節的背景與修法方向
  5. OWASP — Vulnerability Disclosure Cheat Sheet - 安全性缺陷該揭露到什麼程度
  6. JSON Web Tokens - 解 JWT claims 的依據
  7. Toolshop(Practice Software Testing) - 18 份報告的受測站
  8. testsmith-io/practice-software-testing - 兩個版本的原始碼,確認缺陷是刻意植入的
  9. 本專案 output/reports/issues/ - 那 18 份報告
  10. 本專案 20260730-toolshop-bughunt-gaps/candidates.yaml - evidence: [] 那一筆

上一篇
Day 13|看到異常不代表找到 Bug:教同事先分類
下一篇
Day 15|不要再告訴它每一步:從 Test Case 到 Exploration Charter
系列文
Claude × Playwright:30 天打造你的 Agentic SDET 同事16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言