iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
AI Engineering

30 天打造我的 AI 開發工作流:從需求分析到上線系列 第 24 篇

Day 24|系統做完之後:哪些測試給 Claude,哪些一定要自己來

  • 分享至 

  • xImage
  •  

前言

昨天講完稽核軌跡,這個系統該有的功能就齊了:登入、開會、寫記錄、送出版本、確認、生效、指派決議項。

然後問題就來了——全綠代表什麼?

前面每一天,測試都是跟著功能一起長出來的:寫一個功能、配一組測試、綠了就交。那些測試驗的是我當初寫進規格的東西。所以「全綠」嚴格來說只證明了一件事:它做出來的東西,跟我寫下來的東西一致。

至於我寫下來的東西對不對、有沒有漏,測試不會知道。

今天這篇是整理我怎麼切這條線:哪些測試交給 Claude、哪些得自己來、各自怎麼做。


分界線不是難易,是判準從哪裡來

我試過幾種切法。「簡單的給 AI、複雜的自己來」不對——最複雜的並行測試它寫得比我好。「自動化的給 AI、手動的自己來」也不對,那只是在描述工具,不是在分工作。

現在用的判準只有一句話:

判準已經寫在規格或契約裡的,交給 Claude。判準還在我腦子裡的,只能自己測。

這條線之所以有用,是因為它同時給出了「自己測完要做什麼」的答案——把腦子裡的那條判準寫下來,它就變成下一類了。 人測的東西應該越測越少,如果每次都在測同一件事,代表沒有回收。

下面分兩邊講。


交給 Claude 的四種,以及怎麼開

一、分層測試

每個功能自己那一段,這是前面十幾天的常態,不多說。唯一要注意的是它的盲區——等一下第二部分會講。

二、一條龍測試

這個系統有 73 條後端測試,但沒有一條走過使用者真的會走的路。原因是每個測試檔都從半路開始:前置資料是 fixture 直接塞進資料庫的,身分是直接簽一張 token 出來的。

def bearer(user: User) -> dict[str, str]:
    """A real access token for this user."""
    return {"Authorization": f"Bearer {security.issue_access_token(user.id)}"}


# 既有的 45 條測試都透過這個拿身分。換認證的時候改的是這裡,不是那 45 條——
# 它們驗的是業務規則,不是身分機制怎麼傳。
auth = bearer

這樣切是對的。Day 21 換認證的時候,正因為身分收斂在這一個函式,45 條測試一條都沒改。但代價是「怎麼走到那裡」永遠沒有主人。

所以我另外開了一條,給 Claude 的指令是三條限制:

  • 只用 HTTP。 一行 db.add 都不准有。
  • 每一步的輸入,必須是前一步真的回傳的東西。 不准用 fixture 上的 note.id,要用上一個回應 JSON 裡的 id。
  • 從登入開始。 不是從一張簽好的 token 開始。

加上一條關於範圍的:只寫一條。它不涵蓋分支,涵蓋分支是那 73 條的工作。它只回答「接不接得起來」。

第一次跑就紅了,紅在開草稿那一步:

AssertionError: {"detail":{"code":"VALIDATION_ERROR","message":"body: Field required"}}

DraftCreate 兩個欄位都是 optional,但端點簽名是 payload: DraftCreate,沒有預設值——FastAPI 因此要求 request body 本身必須存在。契約沒寫這件事,而前端剛好送了 {},所以從來沒撞到。

三、掃描型的檢查

有一類問題的答案不在任何一條測試裡,而在「把全部檔案排開來看」。這種工作 Claude 做得比人快太多,而且不會看漏。

我請它列出所有端點,標出沒有宣告 CurrentUser 的:

⚠️ GET   /api/users
⚠️ GET   /api/meetings/{meeting_id}
⚠️ GET   /api/notes/{note_id}
⚠️ GET   /api/notes/{note_id}/versions
⚠️ GET   /api/notes/{note_id}/draft
⚠️ GET   /api/notes/{note_id}/action-items
⚠️ GET   /api/versions/{version_id}
⚠️ POST  /api/meetings
⚠️ POST  /api/auth/login

最後一條是應該的——契約寫得很清楚,login 是「全系統唯一不需要 token 的端點」。其他八條不是。

這一類的訣竅是:要它產出清單,不要它產出結論。 「幫我檢查有沒有安全問題」會得到一篇作文;「列出所有端點與它們宣告的 dependency」會得到一張可以自己看的表。

四、契約兩側對拉

前端有六個 zod schema,是契約在前端那一側的副本。前端測試全部走 MSW mock——但那些 mock 是照著 schema 寫的,所以測的是「我寫的假資料符合我寫的 schema」,永遠會過。

做法是讓後端的一條龍測試把真實回應存成 JSON,前端拿去餵自己的 schema:

Test Files  1 passed (1)
     Tests  6 passed (6)

六個全過,契約兩側是對齊的。重點不是結果,是這件事之前沒有人做過,而它只花了 30 行。


自己測的三種

一、規格有兩種讀法的時候

這是最重要的一種,因為它的失敗方式最隱蔽。

我把端點掃描的結果實際打了一次,確認不是靜態分析誤判:

🔓 放行    GET   /api/users
🔓 放行    GET   /api/notes/{id}
🔓 放行    GET   /api/notes/{id}/versions
🔓 放行    GET   /api/versions/{id}
🔓 放行    GET   /api/notes/{id}/action-items
🔒 MISSING_TOKEN  GET   /api/users/me
🔓 放行    POST  /api/meetings

GET /api/versions/{id} 回的是版本全文、必要確認人名單、完整的決策時間軸。這整個系統存在的理由就是那份東西,它不需要任何憑證就讀得到。最後一條還是寫入——不帶 token 可以開一場會。

為什麼 73 條測試沒有一條紅?我的契約裡,這些端點的權限欄寫的是**「任何人」**。我的意思是「任何登入的使用者」。實作出來是「任何人」。

而測試也是照同一份契約寫的。

Claude 讀規格寫實作,也讀同一份規格寫測試。規格有兩種讀法的時候,它兩邊會用同一種讀法。 一致不等於正確——它只是把同一個誤解執行了兩次。

這就是為什麼「叫 AI 自己寫測試驗自己的實作」有天花板。不是它不夠仔細,是它兩次的輸入是同一份文件。要戳破它,只能有人拿著不在那份文件裡的東西去打——例如「如果完全不帶 token 會怎樣」。契約沒有回答這個問題,所以兩邊都沒有處理它。

二、沒有寫進任何地方的判準

跑完整套測試,最後一行是:

74 passed, 455 warnings in 8.82s

455 個。往上翻:

InsecureKeyLengthWarning: The HMAC key is 21 bytes long, which is below the
minimum recommended length of 32 bytes for SHA256. See RFC 7518 Section 3.2.

簽章金鑰只有 21 bytes。原因是 .env 沒設 JWT_SECRET,吃到程式裡的預設值 "dev-only-not-a-secret"——那個值刻意寫成一看就知道是假的,但沒人注意到它連長度都不夠。這個 warning 從認證做完那天就在印了。

沒有任何一份文件說過「warning 數要是零」。所以它不屬於任何人的驗收條件。綠燈旁邊的數字是不會有人讀的——它不是紅的,所以它不在任何人的路徑上。

同一類的還有:測試跑多久(從 15 秒變 94 秒那次是 argon2)、log 裡有沒有莫名其妙的東西、畫面卡不卡。這些都要人自己去看一眼。

三、「這是我要的嗎」

最後一種其實最難寫成步驟,但不能不做:打開來用一次,問自己「這個東西做出來,是不是我當初想要的」。不是「符不符合規格」——那個 Claude 已經驗過了,而且驗得比我仔細。是規格本身對不對。


結論

  • AI 寫實作和 AI 寫測試,讀的是同一份文件。 文件模糊的地方,兩邊會用同一種讀法,然後一起綠。這是結構性的,不是品質問題。
  • 要它產出清單,不要它產出結論。
  • 人測的量應該逐週下降。

明天:把今天這幾個洞交給 AI 去 review,然後驗證 review 本身。今天的發現是靠「換一個視角從外面打」找到的——而 AI review 最常見的失敗,剛好也是它只看得到你指給它看的那一面。


上一篇
Day 23|稽核軌跡
下一篇
Day 25|AI Code Review 的正確用法,以及怎麼驗證 review 本身
系列文
30 天打造我的 AI 開發工作流:從需求分析到上線 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言