Go語言高級竅門與技巧,看完你也能寫出高質量代碼

在閱讀這篇之前,我希望你已經知道如何做表格驅動的測試以及使用 interface 進行 模擬 (mock)/ 樁 (stub) 注入。這裡是一些竅門:

竅門 1. 不使用框架

來自 Ben Johnson 的竅門。Go 有一個著實很棒的測試框架,它讓你能夠使用同樣的語言編寫測試代碼,無需學習任何的庫或測試引擎,直接用!也可以參考 Ben Johnson 的幫助函數[7],可以幫你省一些代碼行數。

竅門 2. 使用 "_test" 包名

Ben Johnson's tip. Using *_test package doesn't allow you to enter unexported identifiers. This puts you into position of a package's user, allowing you to check whether package's public API is useful.

來自 Ben Johnson 的提示。使用 *_test 包名,你無法使用包中未導出的標識符。這樣在測試中,你就是包的使用者,讓你更好地檢查包的公有 API 是否可用。

竅門 3. 避免全局常量

來自 Mitchell Hashimoto 的竅門。如果你使用了全局的常量標識符,測試時將無法改變它們的行為。這個竅門的例外是全局常量對默認值是有用的。查看下面的例子:

這裡是一些技巧,希望能夠讓你的測試代碼更好:

技巧 1. Test fixtures

這個技巧在標準庫[8] 中用到。這是我從 Mitchell Hashimoto 和 Dave Cheney 的作品中學到的。go test 很好地支持從文件中加載測試數據。第一,go build 忽略名為

testdata 的文件夾。第二,當 ge test 運行時,它將當前目錄設置為包目錄。這使得你可以使用相對路徑 testdata 目錄作為存儲和加載數據的地方。這是一個例子:

技巧 2. Golden 文件

這個技巧也在標準庫[9] 中被用到,但我是從 Mitchell Hashimoto 的演講中學到的。這裡的思想是將期望輸出存儲在一個名為 .golden 的文件中並提供一個 flag 來更新它。這裡是例子:

這個技巧使你得以測試複雜的輸出而無需硬編碼。

技巧 3. 測試幫助函數

Mitchell Hashimoto 的技巧。有時候測試代碼變得有點複雜。當你需要為測試案例做合適的配置,經常包含很多無關的 err 檢查,例如檢查測試文件是否正確加載,檢查數據是否可以解析成 JSON,等等。這些代碼很快就變得十分醜陋!

為了解決這個問題,你需要將無關的代碼分離到幫助函數里。這些函數永遠不應該返回 error,如果有一些操作失敗,應該將 *testing.T 作為參數並讓測試失敗。

還有,如果你的幫助函數需要在後面做一些清理,你應該返回一個做清理工作的函數。看看下面的例子:

( 注:這個例子摘自 Mitchell Hashimoto - Advanced Testing with Go[10] 的演講 )。這個例子裡用到了另一個很酷的技巧就是 defer。在這段代碼中 defer testChdir(t, “ /other")()執行 testChdir 函數並將其返回的清理函數延遲執行。

技巧 4. 子進程:真實調用

有時候你需要測試依賴於可執行程序的代碼。例如,你的程序使用 Git。測試這段代碼的一個辦法是模擬 Git 的行為,但那真的很難!另一個辦法是真正調用 Git 可執行程序。但如果執行測試的用戶沒有安裝 Git 怎麼辦?

這個技巧解決了檢查系統是否有安裝 Git 的問題,如果沒有安裝則跳過測試。例子如下:

( 注:這個例子摘自 Mitchell Hashimoto - Advanced Testing with Go[11] 的演講。)

技巧 5. 子進程:模擬

Andrew Gerrand / Mitchell Hashimoto 的技巧。下面的技巧讓我們模擬一個子進程,無需跳過測試代碼。這個例子也可以在 標準庫測試[12] 中看到。假設我們要測試 Git 失敗的場景。看看這個例子:

這裡的思想把 Go 測試框架作為稍做修改(os.Args[0]- 是生成的 Go test 二進制)的子進程運行。稍做修改是為了在環境變量為 BE_CRASHING_GIT=1 時運行同樣的測試(-test.run=TestFailingGit 的部分),這樣你可以區分何時作為子進程運行,何時正常執行。

技巧 6. 將模擬、幫助函數放在 testing.go 文件中

Hashimoto 提的一個有趣的建議是將幫助函數,fixtures,樁都導出並放在 testing.go 文件中。(注意 testing.go 文件被當成正常的代碼對待,而不是測試代碼。)這使你可以在不同的包中使用模擬和幫助函數,包的使用者在他們的代碼中也可以使用它們。

技巧 7. 處理那些運行慢的測試

Peter Bourgon 的技巧。當你有一些運行很慢的測試時,等待所有測試完成會變得很煩人,特別是當你想立刻知道編譯是否成功時。這個問題的解決辦法是將那些運行慢的測試移到 *_integration_test.go 文件中並在文件的開頭添加編譯選項。例如:

這樣 go test 就不會包含有編譯選項的那些測試。為了執行它們,你需要在 go test 命令中指定編譯選項。

我個人使用 alias,用於運行當前包以及子包裡除 vendor 目錄以外的所有測試。

這個 alias 兼容 -v 選項。

Go語言高級竅門與技巧,看完你也能寫出高質量代碼


分享到:


相關文章: