本系列原發表於 iThome 鐵人賽,本 repository 收錄所有文章範例程式碼,歡迎參考與練習。
這是一個完整的 Go 語言測試驅動開發 (Test-Driven Development, TDD) 學習資源,透過 30 天的循序漸進課程,從基礎概念到實戰應用,幫助開發者掌握 TDD 的核心技能。
- 深入理解 TDD 的核心概念:What, Why, How
- 掌握 Go 語言的測試工具和最佳實踐
- 學會編寫高品質、可維護的測試程式碼
- 熟練運用 Mock 和 Stub 處理外部依賴
- 培養 TDD 開發習慣和思維模式
這個階段的目標是:用最精簡的方式建立 TDD 的核心思想,並快速備妥 Golang 的測試軍火庫。
- Day 1: TDD 濃縮精華 - 一日搞懂 What, Why, How & 關鍵心法
- 內容 (濃縮版):
- 開賽宣言 & What: 介紹系列主軸,直接破題 TDD 的「紅燈 -> 綠燈 -> 重構」黃金循環。
- Why (The Motivation): 綜合闡述 TDD 為何是開發者的「安全網」與「活文件」,它如何提升程式碼品質並讓我們能自信重構。
- How (The Dance): 簡潔地說明「紅-綠-重構」每一步的具體操作與心態,強調「最小化」原則。
- 關鍵心態: 破除「TDD 會拖慢開發速度」等一兩個核心迷思,為讀者建立正確的起步心態。
- 內容 (濃縮版):
- Day 2: 工欲善其事 - 搭建 Golang 開發與測試環境
- 內容: 指導安裝 Golang、VS Code 與推薦的 Go 開發套件,確保開發環境順暢。
- Day 3: Golang 語法速成 - 打造「可測試」的函式、結構與介面
- 內容: 針對 TDD 會用到的核心語法進行快速複習,重點在於如何設計出易於測試的程式碼結構。
- Day 4: Golang 的測試利器 - go test 指令與 _test.go 檔案
- 內容: 介紹 Golang 內建的測試工具,包含 go test 指令的各種實用參數 (-v, -run, -cover)。
- Day 5: 寫出優雅的斷言 - testing 套件與 stretchr/testify
- 內容: 解釋如何使用 testing.T,並重點引入 testify/assert 和 testify/require,讓測試案例的語意更清晰、更易讀。
- Day 6: 表格驅動測試 (Table-Driven Tests) - Go 語言的測試慣用法
- 內容: 介紹並實作 Go 測試中非常常見且強大的模式——表格驅動測試,學習如何用一個測試函式覆蓋多種情境。
- Day 7: 處理依賴 - 測試中的 Mock 與 Stub 基礎
- 內容: 討論當程式碼依賴外部服務 (如資料庫、API) 時該如何測試。介紹 Mock 和 Stub 的概念,並使用 stretchr/testify/mock 進行簡單示範。
這個階段將透過經典的 Kata 練習,將理論內化為開發的肌肉記憶。
- Day 8: 專案啟動 - 設定我們的 Kata 專案結構 (Go Modules)
- 內容: 建立一個全新的 Go Modules 專案,規劃好目錄結構,為接下來的實戰做準備。
- Day 9: Kata 演練:FizzBuzz (一) - 寫下第一個失敗的測試 (紅燈)
- 內容: 完整示範如何根據最小需求,撰寫第一個「恰好會失敗」的測試案例。
- Day 10: Kata 演練:FizzBuzz (二) - 最簡單的實作與重構 (綠燈 -> 重構)
- 內容: 撰寫「恰好能通過測試」的最精簡程式碼,並立即進行重構,完成一次完整的 TDD 循環。
- Day 11: Kata 演練:FizzBuzz (三) - 透過 TDD 逐步疊加功能
- 內容: 示範如何透過新增測試案例,來「驅動」出更完整的功能(例如加入 Buzz 和 FizzBuzz 的邏輯)。
- Day 12: 進階 Kata:字串計算機 (一) - 處理簡單輸入與邊界
- 內容: 介紹 String Calculator Kata。透過 TDD 處理基本需求,例如空字串、單一數字、兩個數字相加。
- Day 13: 進階 Kata:字串計算機 (二) - 應對變化的需求
- 內容: 引入更複雜的需求(例如允許多個數字、處理換行符),展示 TDD 如何幫助我們優雅地應對需求變更。
- Day 14: 進階 Kata:字串計算機 (三) - 處理錯誤與自訂規則
- 內容: 繼續疊加需求,例如不允許負數(需拋出錯誤)、支援自訂分隔符,體會 TDD 在複雜邏輯下的威力。
- Day 15: TDD 實戰回顧 - 我們從 Kata 中學到了什麼?
- 內容: 總結手動 TDD 的經驗,反思這個流程如何影響我們的設計思維,為下一階段的 AI 協作鋪路。
- Day 16: 何時不該用 TDD?誠實面對其限制與權衡
- 內容: (原 Day 5 和 Day 7 的部分內容) 更深入地探討不適合 TDD 的情境(探索性原型、UI視覺調整),以及如何在既有專案 (Legacy Code) 中逐步引入測試的策略。
本系列亮點,將 AI 工具引入 TDD 流程,探索人機協作的新模式。
- Day 17: 迎接 AI 隊友 - 設定 GitHub Copilot / ChatGPT 協作環境
- 內容: 介紹如何在 VS Code 中整合 AI,並分享設定與互動技巧。
- Day 18: AI 詠唱術 (Prompt Engineering) - 如何下達讓 AI 理解 TDD 的指令
- 內容: TDD 的精髓在於精確定義需求,這與 Prompt 的精神不謀而合。分享撰寫精準 Prompt 的技巧。
- Day 19: AI 生成測試 - 由需求直達「紅燈」
- 內容: 示範如何用自然語言描述一個功能,讓 AI 直接產出第一份會失敗的測試程式碼。
- Day 20: AI 生成實作 - 由「紅燈」快速轉「綠」
- 內容: 將失敗的測試交給 AI,讓它自動生成能通過測試的產品程式碼。
- Day 21: AI 輔助重構 - 讓 AI 成為你的程式碼審查員 (Code Reviewer)
- 內容: 將通過測試的程式碼交給 AI,請它提出重構建議、尋找潜在的壞味道 (Code Smells) 或改進命名。
- Day 22: 案例實戰:用 AI+TDD 開發一個簡易 RESTful API (一) - 需求與測試
- 內容: 設定一個具體目標 (例如 GET /users/{id}),指導 AI 根據 OpenAPI 規格或自然語言描述生成完整的測試案例(包含成功、找不到、錯誤請求等)。
- Day 23: 案例實戰:用 AI+TDD 開發一個簡易 RESTful API (二) - 實作與重構
- 內容: 利用 AI 根據測試來生成 API Handler 的程式碼,並在測試通過後,與 AI 一起探討如何重構,例如分層、錯誤處理等。
- Day 24: 人機協作的藝術 - 當 AI 的建議與你想法不同時
- 內容: 討論如何批判性地看待 AI 的產出。我們的價值在於更高層次的設計決策與判斷,學習如何引導 AI、修正 AI。
- Day 25: AI 的超能力 - 利用 AI 處理惱人的邊界條件與錯誤處理
- 內容: AI 特別擅長考慮各種可能性。示範如何請 AI 幫我們「想出」並生成針對各種邊界條件的測試案例。
- Day 26: 舊程式碼的救贖 (一) - 什麼是「特性測試」 (Characterization Tests)
- 內容: 介紹為既有程式碼建立保護網的「特性測試」概念,這是將 TDD 導入舊專案的關鍵第一步。
- Day 27: 舊程式碼的救贖 (二) - 讓 AI 讀懂程式碼並生成測試
- 內容: 示範如何提供一段沒有測試的舊程式碼給 AI,讓它分析並生成描述其「現有行為」的測試,為重構上好保險。
- Day 28: AI 開發的倫理、版權與未來展望
- 內容: 深入討論在專業開發中使用 AI 的重要議題,包括程式碼的版權歸屬、資料隱私,以及對開發者技能的長遠影響。
- Day 29: 案例研究 - AI+TDD 在真實專案中的整合
- 內容: 總結並舉例說明此流程在實際工作中的應用場景,例如開發一個新的微服務、修復一個 Bug、或為一個現有模組添加新功能。
- Day 30: 完賽總結 - 我的開發流程,從此進化
- 內容: 回顧這 30 天的旅程,從 TDD 的心法到 AI 協作的技法,總結收穫並展望人機協作下的開發新範式。
- Go 1.21 或更新版本
- Git
- 任何文字編輯器或 IDE (推薦 VS Code 搭配 Go 擴充)
-
複製專案
git clone https://github.com/CodeMachine0121/Ithome--0-1-AI-Golang-TDD-.git cd ai_tdd -
執行範例程式碼
# 進入任一天的專案目錄 cd projects/day2 # 執行測試 go test # 執行測試並顯示詳細資訊 go test -v
-
安裝相關套件
# 安裝 testify (Day 5 開始使用) go mod tidy
- 🔴 Red: 先寫測試,讓它失敗
- 🟢 Green: 寫最少的程式碼讓測試通過
- 🔵 Refactor: 重構程式碼,保持測試通過
- Stub: 提供固定回傳值的替身
- Mock: 驗證互動行為的間諜
go test: Go 官方測試指令testify: 強大的測試斷言庫testify/mock: 專業的 Mock 工具
func TestFunctionName_Scenario_ExpectedBehavior(t *testing.T) { // 測試內容 }func TestExample(t *testing.T) { // Arrange: 準備測試資料 // Act: 執行被測試的功能 // Assert: 驗證結果 }func TestAdd(t *testing.T) { tests := []struct { name string a, b int expected int }{ {"positive numbers", 2, 3, 5}, {"with zero", 0, 5, 5}, {"negative numbers", -1, -2, -3}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := Add(tt.a, tt.b) assert.Equal(t, tt.expected, result) }) } }- 循序漸進: 按照 Day 1-30 的順序學習
- 動手實作: 每個概念都要親自寫程式碼驗證
- 理解原理: 不只學會怎麼做,更要理解為什麼這麼做
- 持續練習: TDD 是一種習慣,需要不斷練習才能熟練
如果你發現任何問題或有改進建議,歡迎:
- 開啟 Issue
- 提交 Pull Request
開始你的 TDD 之旅吧! 🚀
記住:TDD 不只是一種測試技術,更是一種設計方法和開發哲學。