在 Day 9,我們成功地點亮了第一盞紅燈,我們為 FizzBuzz 問題定義了最簡單的一個案例: Generate(1)
應該回傳 "1",並編寫了一個會失敗的測試來驗證它。
我們現在有了一個清晰的、由測試定義的目標,這盞紅燈就像導航系統上的目的地,指引著我們前進。
今天的目標:完成我們的第一個 TDD 循環!
面對昨天的失敗報告(預期 "1",實際 ""),我們的任務非常明確:修改 Generate
函式,讓它在接收到 1 時,能回傳 "1"。
此刻,TDD 的紀律再次提醒我們:
只做最簡單能奏效的事 (The Simplest Thing That Could Possibly Work)。
一個急於求成的開發者可能會想:「哦,我知道了,我需要把整數轉換成字串。」然後馬上去查 Go 語言的 strconv 套件,並把所有轉換邏輯都寫好。
但一個嚴格的 TDD 實踐者會問一個更「笨」的問題:「什麼是最簡單的程式碼,能讓 Generate(1) 回傳 "1"?」,答案可能讓你發笑:
// "最笨"但有效的寫法
func Generate(number int) string {
return "1" // 直接寫死答案!
}
這個寫法能讓測試通過嗎?絕對能!但它是不是一個好的通用解法?
當然不是!這就是 TDD 的核心思想:
在「綠燈」階段,我們的唯一目標是讓測試通過,暫時不要考慮任何未來的需求。
這能讓我們保持專注,並確保我們的每一步都是由測試驅動的。當然,對於這個特定問題,將整數轉換為字串是一個同樣簡單且更具通用性的解法,所以我們選擇它。
引入 strconv
套件: 為了將整數 int 轉換為字串 string,我們需要使用 Go 的標準函式庫 strconv
中的 Itoa
,而 Itoa
是 Integer to ASCII 的縮寫。
// fizzbuzz/fizzbuzz.go
package fizzbuzz
import "strconv" // 導入函式庫
// Generate 透過 TDD 來實現
func Generate(number int) string {
// 寫下剛好能讓測試通過的程式碼
return strconv.Itoa(number)
}
程式碼修改完畢。回到終端機,再次執行測試指令:
go test -v ./...
見證奇蹟的時刻到了!你會看到一片令人心曠神怡的綠色:
=== RUN TestFizzBuzzGenerator
=== RUN TestFizzBuzzGenerator/should_return_1_for_number_1
--- PASS: TestFizzBuzzGenerator (0.00s)
--- PASS: TestFizzBuzzGenerator/should_return_1_for_number_1 (0.00s)
PASS
ok go-tdd-kata/fizzbuzz 2.241s
恭喜!你已經成功地將紅燈轉為綠燈! 這證明我們剛剛編寫的產品程式碼,滿足了我們在測試中定義的需求。
現在,我們站在了「綠燈」的安全地帶,這不是我們自己認為的,是測試告訴我們: 程式碼的功能是正確的。此刻,我們可以安心地審視我們的作品,思考:
在不改變外部行為(保持綠燈)的前提下,有沒有辦法讓程式碼的內部結構變得更好?
我們通常會尋找一些 「壞味道」(Code Smells),例如:
讓我們來審視一下我們剛剛的傑作:
func Generate(number int) string {
return strconv.Itoa(number)
}
這段程式碼……坦白說,它已經非常乾淨了。它只有一行,意圖清晰,沒有重複,沒有複雜的邏輯,因此,在這個循環中,我們的重構結論是:無需重構。
這是一個非常重要的認知:「重構」是一個有意識的步驟,而「決定不重構」也是一個有效的、有意識的決策,我們不是為了重構而重構,而是為了讓程式碼變得更好。如果它已經足夠好,那就自信地進入下一個循環。
讓我們回顧一下我們剛剛完成的旅程:
Generate(1)
應回傳 "1"」寫了一個失敗的測試。return strconv.Itoa(number)
讓測試通過。我們完成了一個完整、微小但意義重大的 TDD 循環。我們透過這個過程,為 Generate 函式建立了一個堅實的、可驗證的基礎行為。
今天,我們體驗了從「紅」到「綠」的喜悅,並完成了第一個完整的 TDD 循環。
我們學會了:
這個小小的循環給了我們巨大的信心,因為我們知道,程式碼的行為是被自動化測試所保護的。
預告:Day 11 - Kata 演練:FizzBuzz (三) - 透過 TDD 逐步疊加功能
既然我們已經處理了最普通的情況,下一步是什麼?當然是引入新的規則!明天,我們將再次從「紅燈」開始,新增一個測試案例來驅動出 "Fizz" 的邏輯,將見證 TDD 是如何幫助我們安全、逐步地為系統疊加新功能的。