Go 單元測試基本介紹

贾维斯Echo發表於2024-04-16

目錄
  • 引入
  • 一、單元測試基本介紹
    • 1.1 什麼是單元測試?
    • 1.2 如何寫好單元測試
    • 1.3 單元測試的優點
    • 1.4 單元測試的設計原則
  • 二、Go語言測試
    • 2.1 Go單元測試概要
    • 2.2 Go單元測試基本規範
    • 2.3 一個簡單例子
      • 2.3.1 使用Goland 生成測試檔案
      • 2.3.2 執行單元測試
      • 2.3.3 完善測試用例
      • 2.3.5 迴歸測試
    • 2.4 Goland 直接執行單元測試
    • 2.5 Go Test 命令引數
    • 2.6 執行一個檔案中的單個測試
    • 2.7 測試覆蓋率
    • 2.8 公共的幫助函式(helpers)
  • 三、testing.T的擁有的方法
  • 四、Table Driven 模式
    • 4.1 介紹
    • 4.2 Go 組織測試的方式
    • 4.3 舉個例子
    • 4.4 執行 Table Driven 下的單個測試
  • 五、testify/assert 斷言工具包
    • 5.1 介紹
    • 5.2 安裝
    • 5.3 使用
  • 六、單元測試程式碼模板
  • 七、參考文件

引入

正常的業務開發之後的測試流程,都是先單元測試,後整合測試。

  • 單元測試:針對每一個方法進行的測試,單獨驗證每一個方法的正確性。
  • 整合測試:多個元件合併在一起的測試,驗證各個方法、元件之間配合無誤。

所以一般專案都是開發人員要先搞單元測試,單元測試初步驗證之後,再整合測試。

單元測試驗證了各個方法的基本邏輯之後,整合測試就比較少問題了。

一、單元測試基本介紹

1.1 什麼是單元測試?

單元測試(Unit Tests, UT) 是一個優秀專案不可或缺的一部分,是對軟體中的最小可測試部分進行檢查和驗證。在物件導向程式設計中,最小測試單元通常是一個方法或函式。單元測試通常由開發者編寫,用於驗證程式碼的一個很小的、很具體的功能是否正確。單元測試是自動化測試的一部分,可以頻繁地執行以檢測程式碼的更改是否引入了新的錯誤。

特別是在一些頻繁變動和多人合作開發的專案中尤為重要。你或多或少都會有因為自己的提交,導致應用掛掉或服務當機的經歷。如果這個時候你的修改導致測試用例失敗,你再重新審視自己的修改,發現之前的修改還有一些特殊場景沒有包含,恭喜你減少了一次上庫失誤。也會有這樣的情況,專案很大,啟動環境很複雜,你最佳化了一個函式的效能,或是新增了某個新的特性,如果部署在正式環境上之後再進行測試,成本太高。對於這種場景,幾個小小的測試用例或許就能夠覆蓋大部分的測試場景。而且在開發過程中,效率最高的莫過於所見即所得了,單元測試也能夠幫助你做到這一點,試想一下,假如你一口氣寫完一千行程式碼,debug 的過程也不會輕鬆,如果在這個過程中,對於一些邏輯較為複雜的函式,同時新增一些測試用例,即時確保正確性,最後整合的時候,會是另外一番體驗。

1.2 如何寫好單元測試

首先,學會寫測試用例。比如如何測試單個函式/方法;比如如何做基準測試;比如如何寫出簡潔精煉的測試程式碼;再比如遇到資料庫訪問等的方法呼叫時,如何 mock

然後,寫可測試的程式碼。高內聚,低耦合是軟體工程的原則,同樣,對測試而言,函式/方法寫法不同,測試難度也是不一樣的。職責單一,引數型別簡單,與其他函式耦合度低的函式往往更容易測試。我們經常會說,“這種程式碼沒法測試”,這種時候,就得思考函式的寫法可不可以改得更好一些。為了程式碼可測試而重構是值得的。

1.3 單元測試的優點

單元測試講究的是快速測試、快速修復。

  • 測試該環節中的業務問題,比如說在寫測試的時候,發現業務流程設計得不合理。
  • 測試該環節中的技術問題,比如說nil之類的問題。

單元測試,從理論上來說,你不能依賴任何第三方元件。也就是說,你不能使用MySQL或者Redis

如圖,要快速啟動測試,快速發現BUG,快速修復,快速重測。

1.4 單元測試的設計原則

  1. 每個測試單元必須完全獨立、能單獨執行。
  2. 一個測試單元應只關注一個功能函式,證明它是正確的;
  3. 測試程式碼要能夠快速執行。
  4. 不能為了單元測試而修改已完成的程式碼在編寫程式碼後執行針對本次的單元測試,並執行之前的單元測試用例。
  5. 以保證你後來編寫的程式碼不會破壞任何事情;
  6. 單元測試函式使用長的而且具有描述性的名字,例如都以test_開頭,然後加上具體的函式名字或者功能描述;例如:func_test.go。
  7. 測試程式碼必須具有可讀性。

二、Go語言測試

2.1 Go單元測試概要

Go 語言的單元測試預設採用官方自帶的測試框架,透過引入 testing 包以及 執行 go test 命令來實現單元測試功能。

在原始碼包目錄內,所有以 _test.go 為字尾名的原始檔會被 go test 認定為單元測試的檔案,這些單元測試的檔案不會包含在 go build 的原始碼構建中,而是單獨透過 go test 來編譯並執行。

2.2 Go單元測試基本規範

Go 單元測試的基本規範如下:

  • 每個測試函式都必須匯入 testing 包。測試函式的命名類似func TestName(t *testing.T),入參必須是 *testing.T
  • 測試函式的函式名必須以大寫的 Test 開頭,後面緊跟的函式名,要麼是大寫開關,要麼就是下劃線,比如 func TestName(t *testing.T) 或者 func Test_name(t *testing.T) 都是 ok 的, 但是 func Testname(t *testing.T)不會被檢測到
  • 通常情況下,需要將測試檔案和原始碼放在同一個包內。一般測試檔案的命名,都是 {source_filename}_test.go,比如我們的原始碼檔案是allen.go ,那麼就會在 allen.go 的相同目錄下,再建立一個 allen_test.go 的單元測試檔案去測試 allen.go 檔案裡的相關方法。

當執行 go test 命令時,go test 會遍歷所有的 *_test.go 中符合上述命名規則的函式,然後生成一個臨時的 main 包用於呼叫相應的測試函式,然後構建並執行、報告測試結果,最後清理測試中生成的臨時檔案。

2.3 一個簡單例子

2.3.1 使用Goland 生成測試檔案

我們來建立一個示例,建立名為 add.go的檔案

package main

func Add(a int, b int) int {
	return a + b
}
func Mul(a int, b int) int {
	return a * b
}

這裡藉助Goland給 ADD 函式生成並且編寫測試用例,只需要右鍵點選函式,轉到Generate -> Test for file function(生成函式測試)。

Goland 為我們生成了add_test.go單測檔案

package main

import "testing"

func TestAdd(t *testing.T) {
	type args struct {
		a int
		b int
	}
	tests := []struct {
		name string
		args args
		want int
	}{
		// TODO: Add test cases.
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := Add(tt.args.a, tt.args.b); got != tt.want {
				t.Errorf("Add() = %v, want %v", got, tt.want)
			}
		})
	}
}

2.3.2 執行單元測試

執行 go test,該 package 下所有的測試用例都會被執行。

go test .                                                
ok      gotest  1.060s

go test -v-v 引數會顯示每個用例的測試結果,另外 -cover 引數可以檢視覆蓋率。

go test -v                                               
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok      gotest  1.208s

2.3.3 完善測試用例

接著我們來完善上面的測試用例,程式碼如下:

package main

import "testing"

func TestAdd(t *testing.T) {
	type args struct {
		a int
		b int
	}
	tests := []struct {
		name string
		args args
		want int
	}{
		{
			name: "Adding positive numbers",
			args: args{a: 2, b: 3},
			want: 5,
		},
		{
			name: "Adding negative numbers",
			args: args{a: -2, b: -3},
			want: -5,
		},
		{
			name: "Adding positive and negative numbers",
			args: args{a: 2, b: -3},
			want: -1,
		},
		{
			name: "Adding zero",
			args: args{a: 2, b: 0},
			want: 2,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := Add(tt.args.a, tt.args.b); got != tt.want {
				t.Errorf("Add() = %v, want %v", got, tt.want)
			}
		})
	}
}

2.3.5 迴歸測試

我們修改了程式碼之後僅僅執行那些失敗的測試用例或新引入的測試用例是錯誤且危險的,正確的做法應該是完整執行所有的測試用例,保證不會因為修改程式碼而引入新的問題。

go test -v
=== RUN   TestAdd
=== RUN   TestAdd/Adding_positive_numbers
=== RUN   TestAdd/Adding_negative_numbers
=== RUN   TestAdd/Adding_positive_and_negative_numbers
=== RUN   TestAdd/Adding_zero
--- PASS: TestAdd (0.00s)
    --- PASS: TestAdd/Adding_positive_numbers (0.00s)
    --- PASS: TestAdd/Adding_negative_numbers (0.00s)
    --- PASS: TestAdd/Adding_positive_and_negative_numbers (0.00s)
    --- PASS: TestAdd/Adding_zero (0.00s)
=== RUN   TestMul
=== RUN   TestMul/結果為0
=== RUN   TestMul/結果為-1
=== RUN   TestMul/結果為1
--- PASS: TestMul (0.00s)
    --- PASS: TestMul/結果為0 (0.00s)
    --- PASS: TestMul/結果為-1 (0.00s)
    --- PASS: TestMul/結果為1 (0.00s)
PASS
ok      gotest  0.912s

測試結果表明我們的單元測試全部透過。

2.4 Goland 直接執行單元測試

如果你的測試方法簽名沒錯的話,就能看到這個綠色圖示,點選就能看到很多選項。

最主要的是:

  • Run:執行模式,直接執行整個測試。
  • Debug:Debug模式,你可以打斷點。
  • Run xxx with Coverage:執行並且輸出測試覆蓋率。
  • 其它Profile都是效能分析,很少用。

除非你要看測試覆蓋率,不然都用Debug

2.5 Go Test 命令引數

go test 是 Go 語言的測試工具,你可以使用它來執行 Go 程式的測試函式。

你可以在命令列中使用以下引數來呼叫 go test 命令:

  • -run:指定要執行的測試函式的名稱的正規表示式。例如,使用 go test -run TestAdd 可以執行名稱為 TestSum 的測試函式。
  • -bench:指定要執行的基準測試的名稱的正規表示式。例如,使用 go test -bench . 可以執行所有基準測試。
  • -count:指定要執行測試函式或基準測試的次數。例如,使用 go test -count 2 可以執行測試函式或基準測試兩次。
  • -v:輸出測試函式或基準測試的詳細輸出。
  • -timeout:設定測試函式或基準測試的超時時間。例如,使用 go test -timeout 1s 可以將超時時間設定為 1 秒。

以下是一個go Test命令表格:

引數 說明
-bench regexp 僅執行與正規表示式匹配的基準測試。預設不執行任何基準測試。使用 -bench .-bench= 來執行所有基準測試。
-benchtime t 執行每個基準測試足夠多的迭代,以達到指定的時間 t(例如 -benchtime 1h30s)。預設為1秒(1s)。特殊語法 Nx 表示執行基準測試 N 次(例如 -benchtime 100x)。
-count n 執行每個測試、基準測試和模糊測試 n 次(預設為1次)。如果設定了 -cpu,則為每個 GOMAXPROCS 值執行 n 次。示例總是執行一次。-count 不適用於透過 -fuzz 匹配的模糊測試。
-cover 啟用覆蓋率分析。
-covermode set,count,atomic 設定覆蓋率分析的 mode。預設為 "set",如果啟用了 -race,則為 "atomic"。
-coverpkg pattern1,pattern2,pattern3 對匹配模式的包應用覆蓋率分析。預設情況下,每個測試僅分析正在測試的包。
-cpu 1,2,4 指定一系列的 GOMAXPROCS 值,在這些值上執行測試、基準測試或模糊測試。預設為當前的 GOMAXPROCS 值。-cpu 不適用於透過 -fuzz 匹配的模糊測試。
-failfast 在第一個測試失敗後不啟動新的測試。
-fullpath 在錯誤訊息中顯示完整的檔名。
-fuzz regexp 執行與正規表示式匹配的模糊測試。當指定時,命令列引數必須精確匹配主模組中的一個包,並且正規表示式必須精確匹配該包中的一個模糊測試。
-fuzztime t 在模糊測試期間執行足夠多的模糊目標迭代,以達到指定的時間 t(例如 -fuzztime 1h30s)。預設為永遠執行。特殊語法 Nx 表示執行模糊目標 N 次(例如 -fuzztime 1000x)。
-fuzzminimizetime t 在每次最小化嘗試期間執行足夠多的模糊目標迭代,以達到指定的時間 t(例如 -fuzzminimizetime 30s)。預設為60秒。特殊語法 Nx 表示執行模糊目標 N 次(例如 -fuzzminimizetime 100x)。
-json 以 JSON 格式記錄詳細輸出和測試結果。這以機器可讀的格式呈現 -v 標誌的相同資訊。
-list regexp 列出與正規表示式匹配的測試、基準測試、模糊測試或示例。不會執行任何測試、基準測試、模糊測試或示例。
-parallel n 允許並行執行呼叫 t.Parallel 的測試函式,以及執行種子語料庫時的模糊目標。此標誌的值是同時執行的最大測試數。
-run regexp 僅執行與正規表示式匹配的測試、示例和模糊測試。
-short 告訴長時間執行的測試縮短其執行時間。預設情況下是關閉的,但在 all.bash 中設定,以便在安裝 Go 樹時可以執行健全性檢查,但不花費時間執行詳盡的測試。
-shuffle off,on,N 隨機化測試和基準測試的執行順序。預設情況下是關閉的。如果 -shuffle 設定為 on,則使用系統時鐘種子隨機化器。如果 -shuffle 設定為整數 N,則 N 將用作種子值。在這兩種情況下,種子將報告以便復現。
-skip regexp 僅執行與正規表示式不匹配的測試、示例、模糊測試和基準測試。
-timeout d 如果測試二進位制檔案執行時間超過持續時間 d,則發生 panic。如果 d 為0,則禁用超時。預設為10分鐘(10m)。
-v 詳細輸出:記錄所有執行的測試。即使測試成功,也列印所有來自 LogLogf 呼叫的文字。
-vet list 配置在 "go test" 期間對 "go vet" 的呼叫,以使用由逗號分隔的 vet 檢查列表。如果列表為空,"go test" 使用被認為總是值得解決的精選檢查列表執行 "go vet"。如果列表為

更多可以參考 Go 語言的官方文件或使用 go help test 命令檢視幫助資訊

2.6 執行一個檔案中的單個測試

如果只想執行其中的一個用例,例如 TestAdd,可以用 -run 引數指定,該引數支援萬用字元 *,和部分正規表示式,例如 ^$

go test -run TestAdd -v
=== RUN   TestAdd
=== RUN   TestAdd/Adding_positive_numbers
=== RUN   TestAdd/Adding_negative_numbers
=== RUN   TestAdd/Adding_positive_and_negative_numbers
=== RUN   TestAdd/Adding_zero
--- PASS: TestAdd (0.00s)
    --- PASS: TestAdd/Adding_positive_numbers (0.00s)
    --- PASS: TestAdd/Adding_negative_numbers (0.00s)
    --- PASS: TestAdd/Adding_positive_and_negative_numbers (0.00s)
    --- PASS: TestAdd/Adding_zero (0.00s)
PASS
ok      gotest  1.008s

2.7 測試覆蓋率

測試覆蓋率是指程式碼被測試套件覆蓋的百分比。通常我們使用的都是語句的覆蓋率,也就是在測試中至少被執行一次的程式碼佔總程式碼的比例。在公司內部一般會要求測試覆蓋率達到80%左右。

Go提供內建功能來檢查你的程式碼覆蓋率,即使用go test -cover來檢視測試覆蓋率。

go test -cover
PASS
coverage: 100.0% of statements
ok      gotest  1.381s

還可以使用 -coverprofile 標誌將覆蓋率資料輸出到一個檔案中,然後使用 go tool cover 命令來檢視更詳細的覆蓋率報告。

2.8 公共的幫助函式(helpers)

對一些重複的邏輯,抽取出來作為公共的幫助函式(helpers),可以增加測試程式碼的可讀性和可維護性。 藉助幫助函式,可以讓測試用例的主邏輯看起來更清晰。

例如,我們可以將建立多次使用的邏輯抽取出來:

type addCase struct{ A, B, want int }

func createAddTestCase(t *testing.T, c *addCase) {
	// t.Helper()
	if ans := Add(c.A, c.B); ans != c.want {
		t.Fatalf("%d * %d expected %d, but %d got",
			c.A, c.B, c.want, ans)
	}

}

func TestAdd2(t *testing.T) {
	createAddTestCase(t, &addCase{1, 1, 2})
	createAddTestCase(t, &addCase{2, -3, -1})
	createAddTestCase(t, &addCase{0, -1, 0}) // wrong case
}

在這裡,我們故意建立了一個錯誤的測試用例,執行 go test,用例失敗,會報告錯誤發生的檔案和行號資訊:

go test
--- FAIL: TestAdd2 (0.00s)
    add_test.go:109: 0 * -1 expected 0, but -1 got
FAIL
exit status 1
FAIL    gotest  1.090s

可以看到,錯誤發生在第11行,也就是幫助函式 createAddTestCase 內部。116, 117, 118行都呼叫了該方法,我們第一時間並不能夠確定是哪一行發生了錯誤。有些幫助函式還可能在不同的函式中被呼叫,報錯資訊都在同一處,不方便問題定位。因此,Go 語言在 1.9 版本中引入了 t.Helper(),用於標註該函式是幫助函式,報錯時將輸出幫助函式呼叫者的資訊,而不是幫助函式的內部資訊。

修改 createAddTestCaseV1,呼叫 t.Helper()

type addCaseV1 struct {
	name string
	A, B int
	want int
}

func createAddTestCaseV1(c *addCaseV1, t *testing.T) {
	t.Helper()
	t.Run(c.name, func(t *testing.T) {
		if ans := Add(c.A, c.B); ans != c.want {
			t.Fatalf("%s: %d + %d expected %d, but %d got",
				c.name, c.A, c.B, c.want, ans)
		}
	})
}

func TestAddV1(t *testing.T) {
	createAddTestCaseV1(&addCaseV1{"case 1", 1, 1, 2}, t)
	createAddTestCaseV1(&addCaseV1{"case 2", 2, -3, -1}, t)
	createAddTestCaseV1(&addCaseV1{"case 3", 0, -1, 0}, t)
}

執行 go test,報錯資訊如下,可以非常清晰地知道,錯誤發生在第 131 行。

go test
--- FAIL: TestAddV1 (0.00s)
    --- FAIL: TestAddV1/case_3 (0.00s)
        add_test.go:131: case 3: 0 + -1 expected 0, but -1 got
FAIL
exit status 1
FAIL    gotest  0.434s

關於 helper 函式的 2 個建議:

  • 不要返回錯誤, 幫助函式內部直接使用 t.Errort.Fatal 即可,在用例主邏輯中不會因為太多的錯誤處理程式碼,影響可讀性。
  • 呼叫 t.Helper() 讓報錯資訊更準確,有助於定位。

當然,如果你是用Goland 編輯器的話,可以不使用t.Helper(),自動會幫你列印出錯誤詳細資訊

三、testing.T的擁有的方法

以下是提供的 *testing.T 型別的方法及其用途的註釋:

// T 是 Go 語言測試框架中的一個結構體型別,它提供了用於編寫測試的方法。
// 它通常透過測試函式的引數傳遞給測試函式。

// Cleanup 註冊一個函式,該函式將在測試結束時執行,用於清理測試過程中建立的資源。
func (c *T) Cleanup(func())

// Error 記錄一個錯誤資訊,但不會立即停止測試的執行。
func (c *T) Error(args ...interface{})

// Errorf 根據 format 和 args 記錄一個格式化的錯誤資訊,但不會立即停止測試的執行。
func (c *T) Errorf(format string, args ...interface{})

// Fail 標記測試函式為失敗,但不會停止當前測試的執行。
func (c *T) Fail()

// FailNow 標記測試函式為失敗,並立即停止當前測試的執行。
func (c *T) FailNow()

// Failed 檢查測試是否失敗。
func (c *T) Failed() bool

// Fatal 記錄一個錯誤資訊,並立即停止測試的執行。
func (c *T) Fatal(args ...interface{})

// Fatalf 記錄一個格式化的錯誤資訊,並立即停止測試的執行。
func (c *T) Fatalf(format string, args ...interface{})

// Helper 標記當前函式為輔助函式,當測試失敗時,輔助函式的檔名和行號將不會顯示在錯誤訊息中。
func (c *T) Helper()

// Log 記錄一些資訊,這些資訊只有在啟用詳細日誌(-v標誌)時才會顯示。
func (c *T) Log(args ...interface{})

// Logf 記錄一些格式化的資訊,這些資訊只有在啟用詳細日誌(-v標誌)時才會顯示。
func (c *T) Logf(format string, args ...interface{})

// Name 返回當前測試或基準測試的名稱。
func (c *T) Name() string

// Skip 標記測試為跳過,並記錄一個錯誤資訊。
func (c *T) Skip(args ...interface{})

// SkipNow 標記測試為跳過,並立即停止當前測試的執行。
func (c *T) SkipNow()

// Skipf 標記測試為跳過,並記錄一個格式化的錯誤資訊。
func (c *T) Skipf(format string, args ...interface{})

// Skipped 檢查測試是否被跳過。
func (c *T) Skipped() bool

// TempDir 返回一個臨時目錄的路徑,該目錄在測試結束時會被自動刪除。
func (c *T) TempDir() string

四、Table Driven 模式

4.1 介紹

Table Driven 模式是一種軟體設計模式,它透過將測試資料儲存在一個表格(通常是結構化的資料結構,如陣列、切片、對映或結構體)中,然後在一個單獨的函式或方法中遍歷這個表格來執行測試。這種模式使得測試程式碼更加模組化、可讀性和可維護性更高。

在 Go 語言中,Table Driven 模式通常透過定義一個結構體來組織測試資料,然後使用一個迴圈來遍歷這個結構體,為每個測試用例執行相同的測試邏輯。這種方法可以很容易地新增新的測試用例,並且可以使測試程式碼更加簡潔和易於維護。

4.2 Go 組織測試的方式

Go裡面,慣常的組織測試的方式,都是用Table Driven

Table Driven的形式如下圖。主要分成三個部分:

  • 測試用例的定義:即每一個測試用例需要有什麼。
  • 具體的測試用例:你設計的每一個測試用例都在這裡。
  • 執行測試用例:這裡面還包括了對測試結果進行斷言。

注意,你要優先使用Table Driven,但是不用強求

你把測試用例定義看做是列名,每一個測試用例就是一行資料,就能理解Table Driven這個含義了。

4.3 舉個例子

func TestMul(t *testing.T) {
	type args struct {
		a int
		b int
	}
	tests := []struct {
		name string
		args args
		want int
	}{
		{
			name: "結果為0",
			args: args{a: 2, b: 0},
			want: 0,
		},
		{
			name: "結果為-1",
			args: args{a: -1, b: 1},
			want: -1,
		},
		{
			name: "結果為1",
			args: args{a: -1, b: -1},
			want: 1,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := Mul(tt.args.a, tt.args.b); got != tt.want {
				t.Errorf("Mul() = %v, want %v", got, tt.want)
			}
		})
	}
}

4.4 執行 Table Driven 下的單個測試

當你使用前面 Table Driven 的模式時,可以單個執行測試用例

五、testify/assert 斷言工具包

5.1 介紹

testify/assert 是一個流行的Go語言斷言庫,它提供了一組豐富的斷言函式,用於簡化測試程式碼的編寫。這個庫提供了一種更宣告式的方式來編寫測試,使得測試意圖更加明確,程式碼更加簡潔。

使用 testify/assert 時,您不再需要編寫大量的 if 語句和 Error 方法呼叫來檢查條件和記錄錯誤。相反,您可以使用像 assert.Equalassert.Nilassert.True 這樣的斷言函式來驗證測試的期望結果。

5.2 安裝

go get github.com/stretchr/testify

5.3 使用

斷言包提供了一些有用的方法,可以幫助您在Go語言中編寫更好的測試程式碼。

  • 列印友好、易於閱讀的失敗描述
  • 允許編寫可讀性強的程式碼
  • 可以為每個斷言新增可選的註釋資訊
    看看它的實際應用:
package yours
import (
  "testing"
  "github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {
  // 斷言相等
  assert.Equal(t, 123, 123, "它們應該相等")
  // 斷言不等
  assert.NotEqual(t, 123, 456, "它們不應該相等")
  // 斷言為nil(適用於錯誤處理)
  assert.Nil(t, object)
  // 斷言不為nil(當你期望得到某個結果時使用)
  if assert.NotNil(t, object) {
    // 現在我們知道object不是nil,我們可以安全地進行
    // 進一步的斷言而不會引起任何錯誤
    assert.Equal(t, "Something", object.Value)
  }
}

每個斷言函式都接受 testing.T 物件作為第一個引數。這就是它如何透過正常的Go測試能力輸出錯誤資訊的方式。

每個斷言函式都返回一個布林值,指示斷言是否成功。這對於在特定條件下繼續進行進一步的斷言非常有用。

當我們有多個斷言語句時,還可以使用assert := assert.New(t)建立一個assert物件,它擁有前面所有的斷言方法,只是不需要再傳入Testing.T引數了。

package yours
import (
  "testing"
  "github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {
  assert := assert.New(t)
  // 斷言相等
  assert.Equal(123, 123, "它們應該相等")
  // 斷言不等
  assert.NotEqual(123, 456, "它們不應該相等")
  // 斷言為nil(適用於錯誤處理)
  assert.Nil(object)
  // 斷言不為nil(當你期望得到某個結果時使用)
  if assert.NotNil(object) {
    // 現在我們知道object不是nil,我們可以安全地進行
    // 進一步的斷言而不會引起任何錯誤
    assert.Equal("Something", object.Value)
  }
}

在上面的示例中,assert.New(t) 建立了一個新的 assert 例項,然後您可以使用這個例項的方法來進行斷言。如果斷言失敗,testify/assert 會自動標記測試為失敗,並記錄一個詳細的錯誤訊息。

六、單元測試程式碼模板

func Test_Function(t *testing.T) {
	testCases := []struct {
		name string //測試用例的名稱
		args any    //測試用例的輸入引數
		want string //期望的返回值
	}{
		// 測試用例,測試用例表格
		{},
		{},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			//具體的測試程式碼
		})
	}
}

七、參考文件

  • Go Test 單元測試簡明教程
  • Go單元測試入門

相關文章