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,有啥差異


打完收工