測試驅動開發,英文全稱Test-Driven Development,簡稱TDD,是一種不同於傳統軟體開發流程的新型的開發方法。 它要求在編寫某個功能的程式碼之前先編寫測試程式碼,然後只編寫使測試透過的功能程式碼,透過測試來推動整個開發的進行。 這有助於編寫簡潔可用和高質量的程式碼,並加速開發過程。
Hello, YOU
書接上文?上篇文章中,我們嘗試編寫了hello word 函式以及第一個測試 hello_test
package main import "fmt" func Hello() string { return "Hello, world" } func main() { fmt.Println(Hello()) }
package main import "testing" func TestHello(t *testing.T) { got := Hello() want := "Hello, world" if got != want { t.Errorf("got '%q' want '%q'", got, want) } }
package main import "testing" func TestHello(t *testing.T) { got := Hello("Chris") want := "Hello, Chris" if got != want { t.Errorf("got '%q' want '%q'", got, want) } }
這時執行 go test
,你應該會獲得一個編譯錯誤
Hello
來接受一個引數。Hello
func Hello(name string) string { return "Hello, world" }
如果你嘗試再次執行測試,main.go
會編譯失敗,因為你沒有傳遞引數。傳入引數「world」讓它透過。
func main() { fmt.Println(Hello("world")) }
現在,當你執行測試時,你應該看到類似的內容
name
引數並用 Hello,字串連線它
func Hello(name string) string { return "Hello, " + name }
現在再執行測試應該就透過了。
接下來我們可以介紹一下另一種語言特性 常量。
const englishHelloPrefix = "Hello, "
現在我們可以重構程式碼
const englishHelloPrefix = "Hello, " func Hello(name string) string { return englishHelloPrefix + name }
Hello
時建立 "Hello, "
字串例項。再次回到 Hello, world
func TestHello(t *testing.T) { t.Run("saying hello to people", func(t *testing.T) { got := Hello("Chris") want := "Hello, Chris" if got != want { t.Errorf("got '%q' want '%q'", got, want) } }) t.Run("say hello world when an empty string is supplied", func(t *testing.T) { got := Hello("") want := "Hello, World" if got != want { t.Errorf("got '%q' want '%q'", got, want) } }) }
func TestHello(t *testing.T) { assertCorrectMessage := func(t *testing.T, got, want string) { t.Helper() if got != want { t.Errorf("got '%q' want '%q'", got, want) } } t.Run("saying hello to people", func(t *testing.T) { got := Hello("Chris") want := "Hello, Chris" assertCorrectMessage(t, got, want) }) t.Run("empty string defaults to 'world'", func(t *testing.T) { got := Hello("") want := "Hello, World" assertCorrectMessage(t, got, want) }) }
t *testing.T
,這樣我們就可以在需要的時候令測試程式碼失敗。t.Helper()
需要告訴測試套件這個方法是輔助函式(helper)。
if 修復程式碼。
const englishHelloPrefix = "Hello, " func Hello(name string) string { if name == "" { name = "World" } return englishHelloPrefix + name }
如果我們執行測試,應該看到它滿足了新的要求,並且我們沒有意外地破壞其他功能。
先來一個小總結
-
編寫一個測試
-
讓編譯透過
-
執行測試,檢視失敗原因並檢查錯誤訊息是很有意義的
-
編寫足夠的程式碼以使測試透過
-
重構
繼續前進!更多需求
西班牙語
t.Run("in Spanish", func(t *testing.T) { got := Hello("Elodie", "Spanish") want := "Hola, Elodie" assertCorrectMessage(t, got, want) })
先編寫測試。當你嘗試執行測試時,編譯器 應該 會出錯,因為你用兩個引數而不是一個來呼叫 Hello
。
./hello_test.go:27:19: too many arguments in call to Hello have (string, string) want (string)
透過向 Hello
新增另一個字串引數來解決編譯問題
func Hello(name string, language string) string { if name == "" { name = "World" } return englishHelloPrefix + name }
當你嘗試再次執行測試時,它會報錯在其他測試和 hello.go
中沒有傳遞足夠的引數給 Hello
函式
./hello.go:15:19: not enough arguments in call to Hello have (string) want (string, string)
透過傳遞空字串來解決它們。現在,除了我們的新場景外,你的所有測試都應該編譯並透過
hello_test.go:29: got 'Hello, Elodie' want 'Hola, Elodie'
這裡我們可以使用 if
檢查語言是否是「西班牙語」,如果是就修改資訊
func Hello(name string, language string) string { if name == "" { name = "World" } if language == "Spanish" { return "Hola, " + name } return englishHelloPrefix + name }
const spanish = "Spanish" const helloPrefix = "Hello, " const spanishHelloPrefix = "Hola, " func Hello(name string, language string) string { if name == "" { name = "World" } if language == spanish { return spanishHelloPrefix + name } return englishHelloPrefix + name }
法語
-
編寫一個測試,斷言如果你傳遞
"French"
你會得到"Bonjour, "
-
看到它失敗,檢查錯誤資訊是否容易理解
func Hello(name string, language string) string { if name == "" { name = "World" } if language == spanish { return spanishHelloPrefix + name } if language == french { return frenchHelloPrefix + name } return englishHelloPrefix + name }
switch
於是我們自然而然想到了使用 switch
當你有很多 if
語句檢查一個特定的值時,通常使用 switch
語句來代替。如果我們希望稍後新增更多的語言支援,我們可以使用 switch
來重構程式碼,使程式碼更易於閱讀和擴充套件。
func Hello(name string, language string) string { if name == "" { name = "World" } prefix := englishHelloPrefix switch language { case french: prefix = frenchHelloPrefix case spanish: prefix = spanishHelloPrefix } return prefix + name }
編寫一個測試,新增用你選擇的語言寫的hello,你應該可以看到擴充套件這個 神奇 的函式是多麼簡單。
最後一次重構
你可能會抱怨說也許我們的函式正在變得很臃腫。對此最簡單的重構是將一些功能提取到另一個函式中。
func Hello(name string, language string) string { if name == "" { name = "World" } return greetingPrefix(language) + name } func greetingPrefix(language string) (prefix string) { switch language { case french: prefix = frenchHelloPrefix case spanish: prefix = spanishHelloPrefix default: prefix = englishHelloPrefix } return }
-
在我們的函式簽名中,我們使用了 命名返回值(
prefix string
)。
-
這將在你的函式中建立一個名為
prefix
的變數。 -
它將被分配「零」值。這取決於型別,例如
int
是 0,對於字串它是""
。 -
你只需呼叫
return
而不是return prefix
即可返回所設定的值。 -
這將顯示在 Go Doc 中,所以它使你的程式碼更加清晰。
-
如果沒有其他
case
語句匹配,將會執行default
分支。