使用子測(cè)試的主要優(yōu)勢(shì)在于更清晰的測(cè)試組織、靈活的細(xì)粒度控制和獨(dú)立運(yùn)行能力。1. 它允許將相關(guān)測(cè)試用例分組,提高可讀性和可維護(hù)性;2. 可通過命令如 go test -run testmyfunction/zero_input 獨(dú)立運(yùn)行特定子測(cè)試,加快調(diào)試速度;3. 支持參數(shù)化測(cè)試,通過遍歷測(cè)試用例切片實(shí)現(xiàn)多輸入驗(yàn)證,并明確顯示失敗用例;4. 可與基準(zhǔn)測(cè)試結(jié)合,使用 b.run 構(gòu)建不同輸入場(chǎng)景的性能評(píng)估結(jié)構(gòu),幫助識(shí)別性能瓶頸。
golang子測(cè)試(subtests)的主要優(yōu)勢(shì)在于更清晰、更靈活的測(cè)試組織和更細(xì)粒度的測(cè)試控制。它允許你將相關(guān)的測(cè)試用例組織在一起,并且可以獨(dú)立運(yùn)行單個(gè)子測(cè)試,方便調(diào)試和問題定位。
使用 t.Run 可以構(gòu)建分層測(cè)試結(jié)構(gòu),提高測(cè)試代碼的可讀性和可維護(hù)性。
package main import ( "fmt" "testing" ) func TestMyFunction(t *testing.T) { // 基礎(chǔ)測(cè)試 t.Run("Basic Test", func(t *testing.T) { result := myFunction(1) if result != 2 { t.Errorf("Basic test failed, expected 2, got %d", result) } }) // 邊界值測(cè)試 t.Run("Edge Cases", func(t *testing.T) { t.Run("Zero Input", func(t *testing.T) { result := myFunction(0) if result != 1 { t.Errorf("Zero input test failed, expected 1, got %d", result) } }) t.Run("Negative Input", func(t *testing.T) { result := myFunction(-1) if result != 0 { t.Errorf("Negative input test failed, expected 0, got %d", result) } }) }) // 性能測(cè)試(可選) t.Run("Performance Test", func(t *testing.T) { t.Skip("Skipping performance test for now") // 暫時(shí)跳過性能測(cè)試 // 性能測(cè)試代碼... }) } func myFunction(input int) int { return input + 1 } func ExampleMyFunction() { result := myFunction(5) fmt.Println(result) // Output: 6 }
為什么使用子測(cè)試而不是多個(gè)獨(dú)立的測(cè)試函數(shù)?
使用子測(cè)試的主要原因是為了更好地組織和管理測(cè)試用例。想象一下,如果 TestMyFunction 有十幾個(gè)或更多的測(cè)試用例,將它們都寫成獨(dú)立的測(cè)試函數(shù)會(huì)顯得非?;靵y。子測(cè)試允許你將相關(guān)的測(cè)試用例分組,提高代碼的可讀性。此外,通過 t.Run 創(chuàng)建的子測(cè)試可以獨(dú)立運(yùn)行,這在調(diào)試時(shí)非常有用。你可以使用 go test -run TestMyFunction/Zero_Input 這樣的命令來只運(yùn)行 “Zero Input” 這個(gè)子測(cè)試,而不需要運(yùn)行整個(gè)測(cè)試套件。這大大加快了調(diào)試速度。另一方面,獨(dú)立的測(cè)試函數(shù)在數(shù)量增加時(shí)會(huì)造成 *_test.go 文件變得臃腫,難以維護(hù)。
立即學(xué)習(xí)“go語言免費(fèi)學(xué)習(xí)筆記(深入)”;
如何利用子測(cè)試進(jìn)行參數(shù)化測(cè)試?
參數(shù)化測(cè)試是指使用不同的輸入值來運(yùn)行相同的測(cè)試邏輯,以驗(yàn)證代碼在各種情況下的正確性。子測(cè)試非常適合用于實(shí)現(xiàn)參數(shù)化測(cè)試。下面是一個(gè)例子:
func TestMyFunctionParameterized(t *testing.T) { testCases := []struct { name string input int expected int }{ {"Positive Input", 1, 2}, {"Zero Input", 0, 1}, {"Negative Input", -1, 0}, {"Large Positive Input", 100, 101}, {"Large Negative Input", -100, -99}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result := myFunction(tc.input) if result != tc.expected { t.Errorf("Test case %s failed, expected %d, got %d", tc.name, tc.expected, result) } }) } }
在這個(gè)例子中,我們定義了一個(gè) testCases 切片,其中包含了多個(gè)測(cè)試用例。每個(gè)測(cè)試用例都有一個(gè)名稱、輸入值和期望的輸出值。然后,我們使用 for 循環(huán)遍歷 testCases 切片,并為每個(gè)測(cè)試用例創(chuàng)建一個(gè)子測(cè)試。這種方法使得添加、修改或刪除測(cè)試用例變得非常容易。而且,當(dāng)測(cè)試失敗時(shí),你可以清楚地看到哪個(gè)測(cè)試用例失敗了,因?yàn)槊總€(gè)子測(cè)試都有一個(gè)唯一的名稱。
子測(cè)試與基準(zhǔn)測(cè)試 (Benchmark) 結(jié)合使用有什么技巧?
雖然子測(cè)試主要用于單元測(cè)試,但它也可以與基準(zhǔn)測(cè)試結(jié)合使用,以更細(xì)粒度地評(píng)估代碼的性能。例如,你可以使用子測(cè)試來比較不同算法或數(shù)據(jù)結(jié)構(gòu)在不同輸入下的性能。
func BenchmarkMyFunction(b *testing.B) { b.Run("Small Input", func(b *testing.B) { for i := 0; i < b.N; i++ { myFunction(1) } }) b.Run("Large Input", func(b *testing.B) { for i := 0; i < b.N; i++ { myFunction(1000) } }) }
在這個(gè)例子中,我們定義了兩個(gè)基準(zhǔn)測(cè)試:一個(gè)使用小輸入,另一個(gè)使用大輸入。通過運(yùn)行 go test -bench=. 命令,我們可以分別測(cè)量 myFunction 在不同輸入下的性能。這可以幫助我們更好地理解代碼的性能瓶頸,并進(jìn)行優(yōu)化。需要注意的是,在基準(zhǔn)測(cè)試中,b.N 表示基準(zhǔn)測(cè)試的迭代次數(shù),b.N 的值由 go test 命令自動(dòng)調(diào)整,以確?;鶞?zhǔn)測(cè)試的運(yùn)行時(shí)間足夠長(zhǎng),從而獲得更準(zhǔn)確的性能數(shù)據(jù)。