iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
Vibe Coding

老闆不會教你的 Vibe Coding 實戰 30 天系列 第 23 篇

老闆不會教你的 Vibe Coding 實戰 30 天|Day 23:測試安全網

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20261005/20119486dHTteDUv3c.png

前言

「人是不可信的。」

不知道你有沒有聽過這句話?以資安的角度來講,許多資安破口都是人為疏失造成的。

因為人會忘記、會偷懶、會心血來潮,儘管你再怎麼小心,還是會有疏漏。

所以我們這章節將會來介紹一個「人不可信」的概念:測試。

所以「測試」是什麼?

對於許多新手來說,測試應該會是一個相對陌生的詞彙。

但其實你在前面已經有做過這件事情,也就是我一直反覆在講的「驗收」。

你請 AI 幫你修改一個功能或者新增一個功能時,都會要求你反覆依照驗收流程跑過一輪,確定功能沒有出現問題,像是...新增一筆、看清單有沒有出現、重新整理看資料還在不在諸如此類。

所以我們就是要將這個「測試流程」變成自動化、變成一個程式幫我們代勞這些事情。

無法理解這好處嗎?簡單來講,讓你原本需要花好幾小時才能完成的驗收工作,變成只要按一下按鈕就能自動完成。

而且還能夠成為一個所謂的「安全網」,也就是當你修改程式碼時,這個安全網會幫你檢查程式碼有沒有被改壞,不會變成改 A 功能時,B 功能也被改壞了卻不知道。

學會選擇對象

蛤?寫測試還要挑對象?

沒錯,不是所有功能都要寫寫測試,而是挑一個「最痛的」功能去寫測試,這才是最有效率的做法。

所以有哪些是屬於「最痛」的功能呢?以我們的 money-note 來講,最痛的功能就是「算錢」相關的功能。

那有哪些呢?例如...

  • sumAmount: 把這個月每一筆金額加總起來(就是畫面最上面那個總額)
  • buildCategoryStats: 各分類花了多少、佔幾成,統計頁的圓餅圖跟明細都吃它的結果

這邊舉例的這兩個程式碼我知道你可能沒看過,畢竟 Vibe Coding 到底誰會看程式碼呢?(笑)但其實還是有跡可尋,畢竟在 Day13 時候我講過底下這句話:

一個是負責「算」的檔案(我這邊是 src/utils/stats.js)

而且當初我有特別講...

「算」的部分有沒有照要求抽成純函式,而不是全部塞在元件裡?

這些話都是在建立以及規劃給這個章節使用,說是收割也不為過。

那為什麼會特別拿這個來當作學習對象呢?原因很簡單,純粹的函式不會跟畫面、儲存綁在一起,測試起來最簡單,而且功能穩定在綁定到畫面上也比較容易驗收,畢竟結果出來就一翻兩瞪眼而已,反之如果是拿畫面來當作測試,那就不同了。

開始第一步

正常狀況下,我們前面所練習的 money-note 專案是沒有安裝任何與測試相關的套件/框架的,所以這邊接下來我們要請 AI 幫我們加上測試框架,並且寫出測試案例,開始之前,請你不要忘記先輸入 /clear 清掉對話紀錄,避免它讀到前面你跟它的對話內容。

如果到這邊沒有問題的話,就試著輸入下面這段 Prompt,請它幫你加上測試框架並寫測試案例:

請幫我替這個專案加上單元測試,核心重點在算錢的邏輯,畫面先不用。

你必須注意我特別在乎這幾種情況:

- 那個月完全沒有記錄
- 好幾個分類金額一樣的時候,順序不能亂跳
- 百分比顯示整數就好,不要一串小數點

測試框架請你使用 Vitest,並增加一個 npm test 指令,這個指令跑完要自動結束,不要停在那邊等。
寫好測試之後請跑一次,然後原本文件(CLAUDE.md、SPEC.md)裡如果有寫到「沒有安裝測試框架」的地方也一起更新。

那這邊我們先停一下,看一下這一段我寫的 Prompt 有什麼特別的地方。

首先,我並沒有特別提到「sumAmount」或「buildCategoryStats」這兩個函式名稱,而是直接用非常白話文的方式把我很在意的情境講出來,由 AI 自己去分析並找出這兩個函式,然後幫我寫測試案例。

沒想法嗎?你也可以回顧一下我們前面跟 AI 拍板寫的 SPEC.md,裡面有一個區塊:

## 7. 驗收條件

v1 完成的定義:

1. 手機開啟,表單就在畫面上 → 輸入 120 → 點「飲食」→ 存檔,三個動作內記完一筆
2. 新記的那筆立刻出現在清單最上方,本月總額同步增加
3. 圓餅圖的扇形大小與明細的百分比正確反映該月各分類金額
4. 點清單任一列可以修改金額/分類/日期/備註,存檔後清單與統計同步更新
5. 刪除會先跳確認,確認後該筆從清單與統計中消失
6. 重新整理頁面,所有資料還在
7. 切到沒有記錄的月份,顯示空狀態而不是壞掉的圖表
8. 把某筆的日期改成上個月,該筆會從本月消失、出現在上個月
9. 連按 `◀` 切到數個月前,點一次「本月」即回到當前月份;此時「本月」按鈕消失,
   且月份切換列的左右箭頭位置不位移
10. **新增一筆支出後切到統計頁,總支出、記帳筆數與圓餅圖都反映最新資料**
11. 在統計頁切換月份,三個數字與圓餅圖同步換成該月;切分頁不會把月份重置回本月

這邊就可以讓你想想甚至請 AI 參考去怎麼寫成測試案例,而不是讓 AI 自己去腦補,這也是為什麼我會在前面章節花了很多時間介紹 SPEC.md 的重要性,因為它就是你跟 AI 的合約(當然你也可以去翻 docs/plans 看當初的計畫)。

接著它會裝 Vitest、生出測試檔,接著會跟你講結果:

https://ithelp.ithome.com.tw/upload/images/20261005/201194862cY1eALkEP.png

你可以看到我的 AI 產出了三個測試檔案,並且了 57 個案例,你會發現它寫的量遠比你想像的還要多。

你如果去翻一下測試檔案(*.test.js),你會發現 stats.test.js、format.test.js 以及 useRecords.test.js 都有測到,這三個檔案分別對應到算錢、格式化以及資料篩選的邏輯。

Note
通常測試檔案都會加上 .test 這是一個慣例,有些也會放在 src/__tests__ 這個資料夾裡,這些都是為了開發者快速辨識這個檔案是測試檔案而已,沒有特別的意義。

如果你實際使用之後發現數量、測試案例與我不同,這是很正常的,因為 AI 具有隨機性,每次生出來的案例數本來就不會一樣,只是我們盡可能透過 SPEC.md 以及 Prompt 去限制它的行為,讓它生出來的案例是我們要求的邊界範圍。

看懂測試案例

儘管測試案例是給 AI 撰寫,但...你也是要稍微看懂他的測試案例在測什麼、寫什麼,不然你怎麼知道它寫的測試案例是對的?

但基本上測試案例相較於程式碼算是好懂很多,因為大多都是「給什麼東西進去,就期待什麼東西出來」,所以你只要看懂它在測什麼就好。

這邊我就隨機挑一個我這邊的範本(src/utils/format.test.js)給你看:

describe('formatAmount', () => {
  it('0 顯示成 $0', () => {
    expect(formatAmount(0)).toBe('$0')
  })

  it('三位數以內沒有逗號', () => {
    expect(formatAmount(999)).toBe('$999')
  })

  it('千分位逗號,前面加 $', () => {
    expect(formatAmount(1200)).toBe('$1,200')
    expect(formatAmount(12480)).toBe('$12,480')
    expect(formatAmount(1234567)).toBe('$1,234,567')
  })
})

這邊其實就是在測試 formatAmount 這個函式,給它不同的數字進去,然後能不能符合我們的期待(toBe())。

不好懂嗎?沒關係,我逐行解釋一下:

  • describe('formatAmount', ...): 將裡面測試案例分成一組,通常會寫上要測試的函式名稱,這樣你就知道這組案例在測什麼。
  • it('0 顯示成 $0', ...): 這是一條測試案例,裡面會寫上這條案例的描述,通常會寫上「期待結果」。
  • expect(formatAmount(0)).toBe('$0'): 這是一整個測試的核心,其中 expect() 是代表著「我期待」,然後括號裡放的是實際跑出來的結果,toBe() 就是「結果要剛好等於這個」,所以一整句白話文...我期待 formatAmount(0) 跑出來剛好是 $0。

所以測試案例就是一個「給什麼東西進去,就期待什麼東西出來」的描述,相信你應該發現測試會比程式碼好懂很多,因為它就是在描述「這個函式的行為」,而這個行為有沒有符合我們的預期,這就是測試的目的。

Note
在工程開發中,有一套方式叫做「測試驅動開發(TDD)」,就是先寫測試案例,再寫程式碼,最後跑測試看有沒有通過。
這個方式可以讓你在寫程式碼之前就先想清楚這個函式的行為,避免寫出不符合預期的程式碼。

可控失敗練習:讓它失敗一次

那...這邊我們要來刻意犯一下錯誤,沒錯,你沒看錯,我們要刻意犯錯,不然你怎麼知道這個測試是對的呢?而且有這個錯誤案例,你才能夠知道測試的價值。

首先請你對著 AI 輸入以下 Prompt:

請你故意把 formatAmount 改壞,讓「千分位逗號」前面少了一個 $,然後幫我跑一次測試,並將結果吐出來給我。

不出意外的話,你應該會看到 AI 跟你說跑出了測試失敗的結果

https://ithelp.ithome.com.tw/upload/images/20261005/201194862nR4Sgi1cP.png

透過測試這個安全防護網,當你新增或者修改功能時,只要你改完之後沒有去調整測試案例,或者改壞了原本的程式碼,測試就會失敗,這時候你就知道你改壞了什麼,也比較容易去找出問題。

不過看完之後記得把它改回來,不然你的專案就真的壞在那裡了,最簡單的方式就是直接跟 AI 說:

請把剛剛故意改壞的 formatAmount 還原回去,然後再跑一次測試確認全部通過。

那...既然都提到 TDD 了,這邊也畫一張 Mermaid 給你看看正規的 TDD 開發流程長什麼樣子:

https://ithelp.ithome.com.tw/upload/images/20261005/201194865bfPhinMPl.png

試試看:跟昨天的 Hook 接起來

其實不管走到哪一個流程、開發到哪,基本上都會一直重複一個行為,也就是 npm test,但這個行為你可以不需要自己記憶也不需要特別跟 AI 說,畢竟 AI 可能會因為 Context 太長而忘記這件事情。

所以我們就可以利用 Hook 來幫我們自動跑測試,這樣每次 AI 做完一件事情之後,就必定會自動幫你跑一次測試,這樣你就不用擔心忘記跑測試了啦~

但這邊我就不特別解釋怎麼做,你可以自己試試看唷!

結語

那時間也差不多到結尾了,這邊也幫你總結一下:

  • 測試就是把你一路手動驗收的過程變成自動化程式。
  • 不用什麼都測,先測錯了最痛的那一塊。
  • 你不用看得懂程式碼也能指揮 AI 寫測試,Prompt 只要把你在乎的情境講清楚就好。
  • 測試案例比程式碼好懂很多,就是「給什麼進去、期待什麼出來」。

不過也要提醒一下,測試通過不等於功能沒問題,許多畫面上的 UI/UX 還是需要你額外去驗收的唷~


上一篇
老闆不會教你的 Vibe Coding 實戰 30 天|Day 22:Hooks 生命週期觸發器
下一篇
老闆不會教你的 Vibe Coding 實戰 30 天|Day 24:Refactor 還技術債
系列文
老闆不會教你的 Vibe Coding 實戰 30 天 共 24 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言