Test-Driven Development - Go (2)
go
golang
TDD
Test-Driven Development
上一篇我們說明了 TDD 的基本精神跟 Flow
我們找個練習案例來邊做邊說明,這樣可以更容易理解
案例來源: Coding Dojo Link
就如同你接到需求一樣 會有一開始的情境描述
這邊只簡單的列出最後要給出的細節
有五本書,每一本都是 EUR 8
買兩本不同的書,打 95折
買三本不同的書,打 9 折
買四本不同的書,打 8 折
五本都買,打 75 折
特別提醒,買了四本書,三本是不同的可以拿到 9 折,第四本還是原價 EUR 8
如果我們一開始就把所有 Test Case 寫上去
一次寫出符合所有 Case 的 Code
那就失去了 TDD 的用意了
TDD 的概念讓我們每次只專注在一個 Case
只要專注處理當下這一個 Case
在 Repeat 整個 Flow 到所有需要都被實現後
自然也保證了在這些情境下, Code 是正確無誤的
也排除了 Over Design 的可能
接下來就讓我們實際做一次
先建立我們要用的檔案
BookStore.go # 程式碼放置的位置
BookStore_test.go # 測試放置的位置
Flow 1: Write Fail Test Code
func TestBuyOneBook(t *testing.T) {
price := calculatePrice([]int{1}) // 還沒有這個 Function
if price != 8 {
t.Fatal("price should be 8, but got", price)
}
}
我們先寫了測試 測試購買一本書的時候 計算出的金額應該是 8 塊
Flow 2: Run Fail the Test
# tdd
/golang/TDD/BookStore_test.go:7:11: undefined: calculatePrice
FAIL tdd [build failed]
FAIL
當然 我們的測試這時候會是 Fail 的 因為我們還沒有寫任何跟這有關的 Code
如果是 Repeat 到這一個步驟的時候,通常都會錯誤在 func 給出的 return 不符合 Test Case
Flow 3: Write Code to Pass
func calculatePrice(books []int) float32 {
return 8
}
相信大家看到這裡都會有點疑惑,為啥這裡直接 return 8
沒有去判斷傳進來的書本有幾本,內容有啥
照著 TDD 精神,我們只需要實現符合現在的 Test Case 的 Code
我們現在只有 買一本書 八塊錢的這個 Test Case
所以 return 8 解決這一個 Test Case 才是符合只處理當下的 Test Case
Flow 4: Run Pass Test
ok tdd (cached)
在 Repeat 整個 Flow 的時候,實務上通常這裡都會修改很多次
因為在不斷疊加的 Test Case 上,每次修改都要讓所有 Test Case 都通過
是要根據你對這整個需求的理解
切記,只寫符合需求 Test Code,不要為了快速,Over Design
Flow 5: Refactor
這一個步驟,是沒有每一次都要有
是可以根據這個當下的 Code 決定需不需要做 Refactor
你可以調整 Model,可以調整 function 的輸入參數等等
但是,不能修改 Test Func 的判斷式(因為這一個是唯一可以保證你沒有改壞)
要回到 Flow 1 之前,所有的 Test Case 都要是 pass 的
Repeat
Flow 1: Write Fail Test Code
根據需求 兩本不同的書 要打 95 折
也就是說 2 * 8 * 0.95 應該是要得到 15.2
func TestBuyTwoDifferentBook(t *testing.T) {
price := calculatePrice([]int{1, 2})
if price != 15.2 {
t.Fatal("price should be 15.2, but got", price)
}
}
Flow 2: Run Fail the Test
--- FAIL: TestBuyTwoDifferentBook (0.00s)
BookStore_test.go:20: price should be 15.2, but got 8
FAIL
FAIL tdd 0.474s
FAIL
如同期待,我們得到了錯誤,就要繼續下一個步驟
Flow 3: Write Code to Pass
func calculatePrice(books []int) float32 {
count := len(books)
price := float32(count) * 8
if count == 2 {
return price * 0.95
}
return price
}
我們修改了一下 calculatePrice 這一個 func
Flow 4: Run Pass Test
ok tdd (cached)
特別提醒,這裡是要跑全部的 Test Case,不是只有這一個 Repeat 的 Test Case
寫在最後
試著嘗試看看,把整個需求完成
Coding Dojo 上面有完整的 Test Case
希望你可以練習看看 一個一個 Test Case 添加上去
整個完成後,停下來思考一下,這樣的開發模式完成的 Code,跟你原有的開發模式的 Code,有啥差異
打完收工