什么是單元測試
? 在 Go 語言中,單元測試是一種測試方法,用于驗(yàn)證代碼的某個(gè)獨(dú)立單元是否按預(yù)期功能,它的目的是確保代碼的每個(gè)組成部分都在獨(dú)立測試的情況下運(yùn)行正常。
? 在我們對(duì)項(xiàng)目新增一個(gè)新功能時(shí),最好就要養(yǎng)成寫單元測試的好習(xí)慣,這樣可以有助于提高我們代碼的質(zhì)量、可維護(hù)性和可靠性。
? 在 Go 中,單元測試的約定是使用標(biāo)準(zhǔn)庫中的?testing
?包。測試文件通常以?_test.go
?為后綴,然后我們使用?go test ...
?配合一些參數(shù)去進(jìn)行測試,Go 測試工具會(huì)自動(dòng)識(shí)別并運(yùn)行這些文件中那點(diǎn)測試樣例。
go test 的兩種模式
1. 本地模式:執(zhí)行當(dāng)前目錄下的所有測試用例
? go test
2. 列表模式:輸入一個(gè)或多個(gè)目錄,執(zhí)行這些目錄下的測試用例
? go test xx/xx
怎么寫單元測試
? 首先,要寫單元測試,那么肯定需要一個(gè)功能函數(shù)。這里我們借用一下之前文章內(nèi)存緩存系統(tǒng)中使用到的一個(gè)功能函數(shù)?ParseSize
?,它的功能是將用戶的輸入內(nèi)存大小,轉(zhuǎn)換為字節(jié)數(shù)和對(duì)應(yīng)的字符串表示形式,其中還會(huì)涉及到一些輸入不合法的處理,
? 本文講的是如何寫單元測試,這里 ParseSize 的源碼就直接給大家了,如下:
package util
import (
"regexp"
"strconv"
"strings"
"time"
)
const (
B = 1 << (iota * 10)
KB
MB
GB
TB
PB
)
const defaultNum = 100
func ParseSize(size string) (int64, string) {
time.Sleep(time.Nanosecond * 500)
re, _ := regexp.Compile("[0-9]+")
unit := string(re.ReplaceAll([]byte(size), []byte("")))
num, _ := strconv.ParseInt(strings.Replace(size, unit, "", 1), 10, 64)
unit = strings.ToUpper(unit)
var byteNum int64 = 0
switch unit {
case "B":
byteNum = num
case "KB":
byteNum = num * KB
case "MB":
byteNum = num * MB
case "GB":
byteNum = num * GB
case "TB":
byteNum = num * TB
case "PB":
byteNum = num * PB
default:
num = 0
}
if num == 0 {
num = 100
byteNum = num * MB
unit = "MB"
}
sizeStr := strconv.FormatInt(num, 10) + unit
return byteNum, sizeStr
}
?在項(xiàng)目根目錄下創(chuàng)建 util 目錄,然后創(chuàng)建 util.go 文件,將上面的代碼粘貼進(jìn)去就行了。
? 強(qiáng)調(diào)一點(diǎn),上面的 ParseSize 函數(shù)的開頭,我加了一個(gè)睡眠函數(shù),是因?yàn)槲覀兊?ParseSize 函數(shù)的處理邏輯比較簡單,怕執(zhí)行太快,進(jìn)行測試時(shí)顯示時(shí)間為 0 ,所以加了個(gè)睡眠延遲一點(diǎn)時(shí)間,模擬一些比較耗時(shí)功能函數(shù)。
準(zhǔn)備工作
? 同樣,我們先在 util 包下創(chuàng)建 util_test.go 文件。在寫單元測試的時(shí)候,我們通常有兩種方法,一種是在測試函數(shù)里面構(gòu)建匿名結(jié)構(gòu)體來組織數(shù)據(jù),另一種就是在提前構(gòu)建數(shù)據(jù)。前者就是將構(gòu)建數(shù)據(jù)的邏輯寫在測試函數(shù)里,這里不多做介紹,我們要著重講的是第二種。
? 為了方便,我們先定義一個(gè)結(jié)構(gòu)體,并將其實(shí)例化,用于存放我們的數(shù)據(jù):
// 所有的測試用例放在這里頭
var commTestData []commStruct
type commStruct struct {
Group string // 所屬類別
SizeStr string // 輸入大小
ExpectSize int64 // 預(yù)期輸出大小
ExpectSizeStr string // 預(yù)期輸出大小字符串類型
}
- Group:這個(gè)是用于子測試時(shí)分類的依據(jù),關(guān)于子測試后面會(huì)提到,這里先不理會(huì)。
- SizeStr:是對(duì)應(yīng)于我們的 ParseSize 功能函數(shù)的輸入
- ExpectSize、ExpectSizeStr:對(duì)應(yīng)于我們的 ParseSize 功能函數(shù)的輸出
? 在單元測試中,也有一個(gè)?func TestMain(m *testing.M)
入口函數(shù),功能和用法于平時(shí)我們使用的 main 類似。我們可以在這里面為單元測試做一些準(zhǔn)備工作,但需要注意的是:如果我們沒有寫 TestMain 函數(shù),那么測試工具會(huì)直接調(diào)用我們的測試函數(shù),但如果我們寫了 TestMain 函數(shù),就需要在 TestMain 中通過 m.Run() 顯示地調(diào)用測試用例:
// 測試用例的入口函數(shù):可以為測試做一些準(zhǔn)備工作
func TestMain(m *testing.M) {
initCommonData()
m.Run() // 執(zhí)行測試用例
}
func initCommonData() {
commTestData = []commStruct{
{"B", "1b", B, "1B"},
{"B", "100b", 100 * B, "100B"},
{"KB", "1kb", KB, "1KB"},
{"KB", "100KB", 100 * KB, "100KB"},
{"MB", "1Mb", MB, "1MB"},
{"GB", "10Gb", 10 * GB, "10GB"},
{"TB", "1tb", TB, "1TB"},
{"PB", "10PB", 10 * PB, "10PB"},
{"unknown", "1G", 100 * MB, "100MB"},
}
}
上面我們通過 TestMain 函數(shù),提前構(gòu)建好了測試所需要的數(shù)據(jù),避免在不同的測試函數(shù)中重復(fù)構(gòu)建測試用例。
功能測試
? 功能測試是一種驗(yàn)證代碼是否按照規(guī)范和需求進(jìn)行工作的測試,它關(guān)注于測試單個(gè)函數(shù)或方法的功能是否正確,以確保其符合預(yù)期的行為。
? 根據(jù)它的定義,我們就大概知道該怎么寫我們的功能測試了。首先功能測試的函數(shù)簽名是這樣的?func TestFunctionName(t *testing.T)
。我們直接在函數(shù)里面寫邏輯即可,因?yàn)橛泻芏嘟M測試樣例,所以我們肯定要用 for 循環(huán)將所有的樣例拿出來,然后一一進(jìn)行驗(yàn)證,驗(yàn)證的過程就是將該樣例的輸入拿出來執(zhí)行一遍功能函數(shù),然后將結(jié)果與我們的樣例預(yù)期結(jié)果進(jìn)行比對(duì)即可,如下:
// 功能測試
func TestParseSize(t *testing.T) {
testData := commTestData
for _, data := range testData {
size, sizeStr := ParseSize(data.SizeStr)
if size != data.ExpectSize || sizeStr != data.ExpectSizeStr {
t.Errorf("測試結(jié)果不符合預(yù)期:%+v", data)
}
}
}
? 這樣我們就寫好了一個(gè)具備基本功能的功能測試代碼了。我們可以通過命令?go test -v
?去執(zhí)行,輸出如下:
$ go test -v
=== RUN TestParseSize
--- PASS: TestParseSize (0.14s)
PASS
ok main/util 0.178s
? 我們一起來看看這個(gè)輸出:
-
=== RUN TestParseSize
:表示正在運(yùn)行名為?TestParseSize
?的測試函數(shù)。 -
--- PASS: TestParseSize (0.14s)
:表示測試函數(shù)?TestParseSize
?成功通過,用時(shí) 0.14 秒。PASS
?表示測試通過,FAIL
?則表示測試失敗。 -
PASS
:表示整個(gè)測試過程中沒有發(fā)現(xiàn)錯(cuò)誤,所有的測試函數(shù)都成功通過。 -
ok main/util 0.178s
:表示測試包?main/util
?成功通過,總用時(shí)為 0.178 秒。
? 下面我們?cè)賮砜纯垂δ軠y試的子測試。
? 功能測試的子測試,又可以叫做并發(fā)測試,我們可以利用它來加快測試的效率。我們下面以測試樣例中的單位,即 group 字段來將測試樣例分個(gè)組:
testData := make(map[string][]commStruct)
for _, item := range commTestData {
group := item.Group
_, ok := testData[group]
if !ok {
testData[group] = make([]commStruct, 0)
}
testData[group] = append(testData[group], item)
}
? 有了數(shù)據(jù),其實(shí)我們的子測試,就相當(dāng)于對(duì)不同組別分別去進(jìn)行測試。
? 所以首先要用一個(gè) for 循環(huán)拿出不同組別的數(shù)據(jù),去分別運(yùn)行,然后在每個(gè)組別運(yùn)行時(shí),去拿出對(duì)應(yīng)組別的數(shù)據(jù)去做驗(yàn)證即可,代碼如下:
func TestParseSizeSub(t *testing.T) {
if testing.Short() {
t.Skip("跳過測試用例 TestParseSizeSub")
}
// 按照 group 分個(gè)組
testData := make(map[string][]commStruct)
for _, item := range commTestData {
group := item.Group
_, ok := testData[group]
if !ok {
testData[group] = make([]commStruct, 0)
}
testData[group] = append(testData[group], item)
}
// 分組去測試 測試數(shù)據(jù)
for k, _ := range testData {
t.Run(k, func(t *testing.T) {
// 下面的子測試樣例就會(huì)去并行執(zhí)行:通過睡眠可以看出效果
t.Parallel()
for _, data := range testData[k] {
size, sizeStr := ParseSize(data.SizeStr)
if size != data.ExpectSize || sizeStr != data.ExpectSizeStr {
t.Errorf("測試結(jié)果不符合預(yù)期:%+v", data)
}
}
})
}
}
? 細(xì)心的小伙伴一定看到了上面有兩個(gè)點(diǎn)是我們沒講的:
-
if testing.Short()
?這個(gè)是做什么的呢?還記得我們上面介紹參數(shù)的時(shí)候說過嗎,這個(gè)參數(shù)是用來避免一些不必要的測試的,所以如果我們的測試不需要,就可以使用 short 參數(shù)跳過這個(gè)子測試。 -
t.Parallel()
?這個(gè)就是我們子測試并行測試的關(guān)鍵了,只有加了這行代碼,我們的子測試才能進(jìn)行并行測試。
? 下面帶大家看看t.Parallel()
?是不是真的有效果,我們?cè)谧訙y試代碼中加入一個(gè)睡眠時(shí)間,先把?t.Parallel()
?注釋掉:
for k, _ := range testData {
t.Run(k, func(t *testing.T) {
//t.Parallel()
for _, data := range testData[k] {
time.Sleep(time.Second)
size, sizeStr := ParseSize(data.SizeStr)
if size != data.ExpectSize || sizeStr != data.ExpectSizeStr {
t.Errorf("測試結(jié)果不符合預(yù)期:%+v", data)
}
}
})
}
? 然后執(zhí)行命令?go test -v
,可以觀察到子測試的樣例每隔一秒才執(zhí)行一次,最終耗時(shí) 9.367 秒。
$ go test -v
=== RUN TestParseSize
--- PASS: TestParseSize (0.10s)
=== RUN TestParseSizeSub
=== RUN TestParseSizeSub/KB
=== RUN TestParseSizeSub/MB
=== RUN TestParseSizeSub/GB
=== RUN TestParseSizeSub/TB
=== RUN TestParseSizeSub/PB
=== RUN TestParseSizeSub/unknown
=== RUN TestParseSizeSub/B
--- PASS: TestParseSizeSub (9.22s)
--- PASS: TestParseSizeSub/KB (2.05s)
--- PASS: TestParseSizeSub/MB (1.02s)
--- PASS: TestParseSizeSub/GB (1.02s)
--- PASS: TestParseSizeSub/TB (1.03s)
--- PASS: TestParseSizeSub/PB (1.03s)
--- PASS: TestParseSizeSub/unknown (1.03s)
--- PASS: TestParseSizeSub/B (2.04s)
PASS
ok main/util 9.367s
? 我們?cè)侔?t.Parallel()
?的注釋去掉,再執(zhí)行?go test -v
?觀察一下:
$ go test -v
=== RUN TestParseSize
--- PASS: TestParseSize (0.14s)
=== RUN TestParseSizeSub
=== RUN TestParseSizeSub/unknown
=== PAUSE TestParseSizeSub/unknown
=== RUN TestParseSizeSub/B
=== PAUSE TestParseSizeSub/B
=== RUN TestParseSizeSub/KB
=== PAUSE TestParseSizeSub/KB
=== RUN TestParseSizeSub/MB
=== PAUSE TestParseSizeSub/MB
=== RUN TestParseSizeSub/GB
=== PAUSE TestParseSizeSub/GB
=== RUN TestParseSizeSub/TB
=== PAUSE TestParseSizeSub/TB
=== RUN TestParseSizeSub/PB
=== PAUSE TestParseSizeSub/PB
=== CONT TestParseSizeSub/unknown
=== CONT TestParseSizeSub/GB
=== CONT TestParseSizeSub/PB
=== CONT TestParseSizeSub/TB
=== CONT TestParseSizeSub/KB
=== CONT TestParseSizeSub/MB
=== CONT TestParseSizeSub/B
--- PASS: TestParseSizeSub (0.00s)
--- PASS: TestParseSizeSub/TB (1.03s)
--- PASS: TestParseSizeSub/B (1.03s)
--- PASS: TestParseSizeSub/MB (1.03s)
--- PASS: TestParseSizeSub/PB (1.03s)
--- PASS: TestParseSizeSub/KB (1.03s)
--- PASS: TestParseSizeSub/unknown (1.03s)
--- PASS: TestParseSizeSub/GB (1.03s)
PASS
ok main/util 1.210s
? 會(huì)發(fā)現(xiàn)子測試幾乎是同時(shí)打印出來的信息,最終耗時(shí) 1.120s,這就驗(yàn)證了?t.Parallel()
?的作用,也同時(shí)驗(yàn)證了功能測試的子測試的作用。
模糊測試
? 模糊測試是一種隨機(jī)生成輸入數(shù)據(jù)并將其提供給函數(shù)或程序的測試方法,它可以幫助發(fā)現(xiàn)潛在的邊界情況和異常輸入,以檢測代碼的魯棒性。
? 也就是說,模式測試本質(zhì)上也是功能測試,只不過模糊測試的輸入不再是我們提前構(gòu)建好的數(shù)據(jù),而是測試工具根據(jù)我們傳入的參數(shù)類型去幫我們構(gòu)建各種輸入,以此來檢測我們的功能函數(shù)在這種隨機(jī)構(gòu)造的輸入情況下,是否還能照常工作。模糊測試的函數(shù)簽名是func FuzzFunctionName(f *testing.F) {}
,如下:
func FuzzParseSize(f *testing.F) {
// 也就是說,模糊測試,本質(zhì)上也是一個(gè)功能測試。
// 只是輸入的內(nèi)容不再是 data,而是所謂的 a
f.Fuzz(func(t *testing.T, a string) {
size, sizeStr := ParseSize(a)
if size == 0 || sizeStr == "" {
t.Errorf("輸入異常,導(dǎo)致 parsesize 沒拿到正確結(jié)果")
}
})
}
? 然后我們可以通過?go test -fuzz FuzzParseSize
?命令開啟模糊測試,輸出如下:
go test -fuzz FuzzParseSize
warning: starting with empty corpus
fuzz: elapsed: 0s, execs: 0 (0/sec), new interesting: 0 (total: 0)
fuzz: elapsed: 3s, execs: 614 (205/sec), new interesting: 7 (total: 7)
fuzz: elapsed: 6s, execs: 4210 (1194/sec), new interesting: 22 (total: 22)
fuzz: elapsed: 9s, execs: 5579 (456/sec), new interesting: 26 (total: 26)
fuzz: elapsed: 12s, execs: 9227 (1221/sec), new interesting: 35 (total: 35)
fuzz: elapsed: 15s, execs: 14480 (1744/sec), new interesting: 44 (total: 44)
fuzz: elapsed: 18s, execs: 16198 (572/sec), new interesting: 49 (total: 49)
......
-
warning: starting with empty corpus
:這是一個(gè)警告,表示開始時(shí)模糊測試的語料庫(corpus)是空的。語料庫是用來保存歷史模式測試時(shí),出現(xiàn)錯(cuò)誤的樣例。 -
elapsed
:經(jīng)過的時(shí)間 -
execs
:執(zhí)行的測試次數(shù)(平均每秒執(zhí)行多少次) -
new interesting
:新增的隨機(jī)測試輸入個(gè)數(shù) -
total
:本次測試的的輸入樣例個(gè)數(shù)
? 運(yùn)行模糊測試,你會(huì)發(fā)現(xiàn)根本不會(huì)停,只能主動(dòng)去停止,這也是為什么模糊測試只能同時(shí)測試的原因。
? 還有就是上面提到的預(yù)料庫,在運(yùn)行模糊測試時(shí),如果出現(xiàn)了預(yù)期之外的錯(cuò)誤,那就會(huì)將這個(gè)樣例保存到語料庫中,并且在之后每次的模糊測試都會(huì)去運(yùn)行這些出錯(cuò)的樣例。語料庫也是保存在本地的,會(huì)在根目錄下生成一個(gè)對(duì)應(yīng)的文件去存放。
性能測試
? 最后我們?cè)賮砜纯葱阅軠y試,在進(jìn)行性能測試之前,我們需要先將 ParseSize 函數(shù)中的睡眠函數(shù)關(guān)掉,避免影響我們的性能測試。因?yàn)?code>Sleep()?不僅會(huì)讓程序睡眠,還會(huì)做一些其他處理,會(huì)對(duì)我們的性能測試產(chǎn)生不小的影響。
? 待會(huì)我們也可以做一個(gè)測試,然后進(jìn)行一個(gè)對(duì)比。
? 性能測試寫起來?xiàng)l條框框會(huì)比較多,它的函數(shù)簽名是這樣的?func BenchmarkFunctionName(b *testing.B) {}
我們啥也先不管,先來個(gè) for 循環(huán),然后直接調(diào)用我們的 ParseSize 函數(shù):
func BenchmarkParseSize(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseSize("1MB")
}
}
? 這樣,一個(gè)簡易的性能測試就寫完了,我們可以用?go test -bench BenchmarkParseSize
,這里先不注釋 ParseSize 中的睡眠函數(shù),我們看看效果:
go test -bench BenchmarkParseSize
goos: windows
goarch: amd64
pkg: main/util
cpu: AMD Ryzen 7 4800H with Radeon Graphics
BenchmarkParseSize-16 100 15301008 ns/op
BenchmarkParseSizeSub/B-16 100 14830110 ns/op
BenchmarkParseSizeSub/KB-16 100 15324944 ns/op
BenchmarkParseSizeSub/MB-16 100 15445510 ns/op
BenchmarkParseSizeSub/GB-16 100 14851633 ns/op
BenchmarkParseSizeSub/TB-16 100 15136910 ns/op
BenchmarkParseSizeSub/PB-16 100 15281375 ns/op
BenchmarkParseSizeSub/unknown-16 100 15188822 ns/op
PASS
ok main/util 22.495s
? 再將睡眠函數(shù)注釋掉,運(yùn)行同樣的命令,看看效果:
go test -bench BenchmarkParseSize
goos: windows
goarch: amd64
pkg: main/util
cpu: AMD Ryzen 7 4800H with Radeon Graphics
BenchmarkParseSize-16 735984 1603 ns/op
BenchmarkParseSizeSub/B-16 704841 1616 ns/op
BenchmarkParseSizeSub/KB-16 750050 1630 ns/op
BenchmarkParseSizeSub/MB-16 748998 1647 ns/op
BenchmarkParseSizeSub/GB-16 635871 1689 ns/op
BenchmarkParseSizeSub/TB-16 769012 1639 ns/op
BenchmarkParseSizeSub/PB-16 748689 1642 ns/op
BenchmarkParseSizeSub/unknown-16 770593 1620 ns/op
PASS
ok main/util 19.901s
? 我們先來解釋一下各個(gè)參數(shù)代表什么:
-
goos: windows
?和?goarch: amd64
:表示你的操作系統(tǒng)和體系結(jié)構(gòu)。 -
pkg: main/util
:表示正在測試的 Go 包的路徑。 -
cpu: AMD Ryzen 7 4800H with Radeon Graphics
:表示你的 CPU 信息。 -
BenchmarkParseSize-16 735984 1603 ns/op
:表示運(yùn)行了 735984 次,平均每次耗時(shí) 1603 納秒 -
PASS
:表示所有的性能測試都通過 -
ok main/util 19.901s
:表示整個(gè)測試過程消耗了 19.901 秒。
? 可以很明顯的看到,這里兩次測試的平均每次迭代耗時(shí)差了很多個(gè)數(shù)量級(jí),但算上我們的睡眠時(shí)間?time.Sleep(time.Nanosecond * 500)
,也就 500 ns 而已。之所以會(huì)這樣是因?yàn)?time.Sleep
?函數(shù)的調(diào)用對(duì)于測試的結(jié)果會(huì)產(chǎn)生較大的影響,特別是在精度較高的情況,比如我們這里的納米級(jí)別。?time.Sleep
?會(huì)導(dǎo)致當(dāng)前 goroutine 掛起,等待指定的時(shí)間再繼續(xù)執(zhí)行。在測試中,這樣的掛起會(huì)導(dǎo)致每次迭代的耗時(shí)相對(duì)較大,從而影響性能測試的結(jié)果。
? 可能會(huì)有人好奇,為什么平均時(shí)長差了很多,但是總耗時(shí)卻差不多。因?yàn)樵?Go 語言的性能測試中,每個(gè)子測試的迭代次數(shù)數(shù)由測試框架自動(dòng)決定的,它會(huì)根據(jù)自己執(zhí)行時(shí)間的變化動(dòng)態(tài)調(diào)整迭代次數(shù),以保證測試結(jié)果的穩(wěn)定性和可靠性。我們也可以自己使用?-benchtime t
?參數(shù)來配置自己想要的運(yùn)行次數(shù)和時(shí)間。
? 下面我們?cè)賮砜纯凑f說性能測試的子測試。
? 性能測試的子測試,其實(shí)沒有啥明確的使用場景,我們下面所舉的例子,也只是為了寫性能測試子測試而寫子測試,能夠使用的場景也就是需要分組歸類去測試的數(shù)據(jù),比如 B、KB、MB 等相同單位的一組去測試。
? 這樣做的好處是啥?有人肯定會(huì)覺得,可以像功能測試那樣做并行測試。
? 答案是否定的,性能測試的子測試沒有并行機(jī)制。我個(gè)人覺得這樣的好處就是,可以指定只執(zhí)行對(duì)應(yīng)分組的測試用例,比如我們只需要對(duì)某一個(gè)單位的大小進(jìn)行特殊處理,就可以只去執(zhí)行對(duì)應(yīng)分組的測試用例了。
? 然后我們來看看怎么寫,同樣的,需要先對(duì)我們的測試樣例進(jìn)行分組,然后在用 for 對(duì)不同組別的測試樣例分別去運(yùn)行性能測試函數(shù):
func BenchmarkParseSizeSub(b *testing.B) {
testData := make(map[string][]commStruct)
for _, item := range commTestData {
group := item.Group
_, ok := testData[group]
if !ok {
testData[group] = make([]commStruct, 0)
}
testData[group] = append(testData[group], item)
}
for k, _ := range testData {
b.Run(k, func(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseSize(testData[k][0].SizeStr)
}
})
}
}
? 上面代碼需要知道的一點(diǎn),就是在每次運(yùn)行?b.Run()
?的時(shí)候,for 循環(huán)里的測試次數(shù)是測試工具自動(dòng)決定的,我們只需要調(diào)用就可以了。
? 上面就差不多是性能測試的基本寫法了,只不過在一些情況下,比如我們?cè)诿看螠y試時(shí)需要去進(jìn)行一下其他的數(shù)據(jù)準(zhǔn)備,如果不進(jìn)行一些處理,這些準(zhǔn)備數(shù)據(jù)的時(shí)間就可能會(huì)導(dǎo)致我們的性能測試偏差較大:
for k, _ := range testData {
b.Run(k, func(b *testing.B) {
// case1
preBenchmark()
for i := 0; i < b.N; i++ {
// case2
preBenchmark1()
ParseSize(testData[k][0].SizeStr)
}
})
}
func preBenchmark1() {
time.Sleep(10 * time.Second)
}
func preBenchmark2() {
time.Sleep(time.Nanosecond * 500)
}
? 在上述代碼中,我們通過?preBenchmark1
?和?preBenchmark2
?函數(shù)模擬了準(zhǔn)備數(shù)據(jù)等其他操作的耗時(shí),這里就直接告訴大家解決的方法了:文章來源:http://www.zghlxwxcb.cn/news/detail-851267.html
- 對(duì)于?
case1
:可以在數(shù)據(jù)準(zhǔn)備完成后,使用b.ResetTimer()
?重置計(jì)時(shí)器 - 對(duì)于
case2
:可以在準(zhǔn)備數(shù)據(jù)前使用?b.StopTimer()
?將計(jì)時(shí)器暫停,然后在準(zhǔn)備好數(shù)據(jù)后,重新啟動(dòng)計(jì)時(shí)器?b.StartTimer()
,這樣就可以減小誤差。
for k, _ := range testData {
b.Run(k, func(b *testing.B) {
// for 循環(huán)外,可以通過 b.ResetTimer() 來重置
preBenchmark1()
b.ResetTimer()
for i := 0; i < b.N; i++ {
// for 循環(huán)內(nèi),可以通過 b.StopTimer() 和 b.StartTimer() 配合使用,來跳過我們不想統(tǒng)計(jì)的耗時(shí)操作。迫不得已不要使用,測試速度慢
b.StopTimer()
preBenchmark2()
b.StartTimer()
ParseSize(testData[k][0].SizeStr)
}
})
}
? 這里強(qiáng)調(diào)一點(diǎn),上面的解決辦法也只能減緩誤差,并不能真正避免誤差。并且如果你要測試上述代碼的話,記得加上-benchtime
?限制一下執(zhí)行次數(shù),否則會(huì)等很久。文章來源地址http://www.zghlxwxcb.cn/news/detail-851267.html
到了這里,關(guān)于手把手教你寫go單元測試的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!