在 Go 語言中,支持為功能模塊編寫單元測試代碼,繼續以上篇教程構建的計算器項目為例,在 simplemath 包中,我們可以為每一個運算模塊編寫了對應的單元測試代碼。
單元測試文件默認以同一目錄下文件名後綴 _test 作為標識,比如我們在 simplemath 目錄下新建 add_test.go 和 sqrt_test.go 文件,分別為 add.go 和 sqrt.go 編寫單元測試,對應的目錄結構如下:
編寫 add_test.go 代碼如下:
package simplemath
import "testing"
func TestAdd(t *testing.T) {
r := Add(1, 2)
if r != 3 {
t.Errorf("Add(1, 2) failed. Got %d, expected 3.", r)
}
}
以及 sqrt_test.go 代碼如下:
package simplemath
import "testing"
func TestSqrt(t *testing.T) {
v := Sqrt(9)
if v != 3 {
t.Errorf("Sqrt(9) failed. Got %v, expected 3.", v)
}
}
在編寫單元測試時,需要引入 testing 包,你可以將其類比為 PHP 中的 PHPUnit 或 Java 中的 JUnit,我們可以基於該包提供的方法來實現自動化測試,測試方法的格式如下所示:
func TestXXX(t *testing.T) {
// 測試邏輯
}
接下來,怎麼運行這些單元測試呢?這也非常簡單。
在 GoLand 中,你可以選擇要執行單元測試的包,比如這裡的 simplemath,然後通過右鍵快捷菜單依次選擇 Run->go test simplemath:
GoLand 對指定包執行單元測試就可以在 GoLand 底部 Run 窗口中看到測試運行結果:
GoLand 單元測試結果可以看到,運行結果列出了測試的內容、測試結果和測試時間。如果我故意把 add_test.go 的代碼改成這樣的錯誤場景:
func TestAdd(t *testing.T) {
r := Add(1, 2)
if r != 2 {
t.Errorf("Add(1, 2) failed. Got %d, expected 3.", r)
}
}
然後我們再次執行單元測試,這一次,我們可以直接選擇對 add_test.go 執行單元測試,在打開的 add_test.go 文件代碼編輯區域通過右鍵快捷菜單選擇 Run 'add_test.go' 即可:
GoLand 對指定文件執行單元測試當然,你可以可以具體對某個方法進行單元測試,點擊 GoLand 在行號右側為測試方法渲染的綠色測試小圖標,選擇 Run 'TestAdd in calc/simplepath' 即可:
GoLand 對指定方法執行單元測試不管以何種方式運行測試,最終都會得到如下的測試失敗結果:
GoLand 單元測試失敗列印的錯誤信息非常簡潔,卻已經足夠讓開發者快速定位到問題代碼所在的文件和行數,從而在最短的時間內確認是單元測試的問題還是程序的問題。
二、問題定位與代碼調試列印變量當然,對於一些簡單的測試,還可以通過列印變量的方式來定位問題,通常我們在 PHP 中就是這麼做的,比如通過 var_dump、printf、echo 之類的語句或函數列印返回的結果,在 Laravel 框架中還可以通過 dd 或 dump 方法進行簡單高效的變量列印調試,在 Go 語言中,對應的列印函數是前面介紹過的 Printf 或 Println 方法,用於對變量進行格式化輸出(類比 PHP 中的 printf 函數和 print 函數,PHP 沒有提供類似 Println 這樣的方法,但是你可以通過在列印字符串結尾加上 \n 來實現換行),這兩個方法都位於 fmt 格式化包中,我們可以這樣列印變量:
fval := 110.48
ival := 200
sval := "This is a string. "
fmt.Println("The value of fval is", fval)
fmt.Printf("fval=%f, ival=%d, sval=%s\n", fval, ival, sval)
fmt.Printf("fval=%v, ival=%v, sval=%v\n", fval, ival, sval)
對應的輸出結果是:
The value of fval is 110.48
fval=110.480000, ival=200, sval=This is a string.
fval=110.48, ival=200, sval=This is a string.
如果代碼是在線上生產環境執行,列印變量這種定位問題的方式就不合適了,這個時候我們可以通過 log 包提供的方法列印關鍵信息或錯誤信息日誌,方便對線上問題進行追蹤,關於日誌功能後面我們在進階版的工程管理中會詳細介紹,這裡先了解下即可。
IDE 調試如果你是通過 GoLand 進行開發的話,直接在代碼中設置斷點(單擊對應代碼行即可),然後選中要調試的源碼文件,在右鍵下拉菜單中點擊「Debug」對應的選項即可開始對指定文件代碼進行斷點調試:
GoLand 代碼調試進入調試模式後,代碼執行流程會暫停在斷點處。在 GoLand 界面下方控制臺的 Debug 窗口中,可以看到當前程序的堆棧信息,你可以通過手動控制(跳入、跳出、進入下一行、終止調試等)對代碼進行調試,具體操作模式和 PhpStorm、ItelliJ IDEA 差不多:
GDB 調試日常開發使用 GoLand 自帶的代碼調試功能就夠了,如果你想要通過更加 Hacker 的方式調試代碼,可以選擇 GDB。
GDB 是一個由 GNU 開源組織發布的、Unix/Linux 作業系統下的、基於命令行的、功能強大的程序調試工具,Go 語言編譯後的二進位文件支持通過 GDB 進行調試,比如上篇教程通過 go build calc 編譯出來的可執行文件 calc,就可以直接用以下命令以調試模式運行:
gdb calc
註:Windows 系統不支持該工具,Mac 下可以通過 brew install gdb 命令安裝。
然後,你就可以通過 GDB 支持的指令以命令行的方式對 Go 代碼進行調試了,你可以通過 l 指令查看代碼:
要跳到某一行查看通過 l <line> 傳入行數即可:
要為某一行設置斷點可以通過 b <line> 來實現:
然後通過 run 命令來運行程序,如果是在 Mac 系統上,可能會報下面這個錯:
這是因為 Darwin 內核在你沒有特殊權限的情況下,不允許調試其它進程。調試某個進程,意味著你對這個進程有完全的控制權限,所以為了防止被惡意利用,它是默認禁止的。允許 gdb 控制其它進程最好的方法就是用系統信任的證書對它進行籤名,對應的解決方法參考這裡:https://opensource.apple.com/source/lldb/lldb-69/docs/code-signing.txt。
進入下一行可以用 n 指令,列印變量可以用 p <var> 指令傳入變量名。。。更多指令使用我就不深入展開了,因為對於新手來說,不推薦使用 GDB 進行代碼調試,直接使用 GoLand 更友好,不是嗎?如果你想探究 GDB 調試的更多用法,請查看對應的官方文檔 。
小結至此,Go 語言零基礎入門三步曲已經完結,分別是第一個 Go 程序、簡單的工程管理、單元測試及代碼調試,下一篇起,我們將正式開始介紹 Go 語言的語言特性、面向對象編程、並發編程、網絡編程等高級使用指南,你可以通過關注本公眾號關注此系列教程的更新動態,學習過程中有任何問題,可以通過教程下方的評論或加入「Go 語言研習社」與學院君討論:
本系列教程首發在 geekr.dev,你可以點擊頁面左下角閱讀原文連結查看最新更新的教程。