W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
原文鏈接:https://gopl-zh.github.io/ch11/ch11-03.html
就其性質(zhì)而言,測試不可能是完整的。計(jì)算機(jī)科學(xué)家Edsger Dijkstra曾說過:“測試能證明缺陷存在,而無法證明沒有缺陷。”再多的測試也不能證明一個(gè)程序沒有BUG。在最好的情況下,測試可以增強(qiáng)我們的信心:代碼在很多重要場景下是可以正常工作的。
對待測程序執(zhí)行的測試的程度稱為測試的覆蓋率。測試覆蓋率并不能量化——即使最簡單的程序的動(dòng)態(tài)也是難以精確測量的——但是有啟發(fā)式方法來幫助我們編寫有效的測試代碼。
這些啟發(fā)式方法中,語句的覆蓋率是最簡單和最廣泛使用的。語句的覆蓋率是指在測試中至少被運(yùn)行一次的代碼占總代碼數(shù)的比例。在本節(jié)中,我們使用go test
命令中集成的測試覆蓋率工具,來度量下面代碼的測試覆蓋率,幫助我們識別測試和我們期望間的差距。
下面的代碼是一個(gè)表格驅(qū)動(dòng)的測試,用于測試第七章的表達(dá)式求值程序:
gopl.io/ch7/eval
func TestCoverage(t *testing.T) {
var tests = []struct {
input string
env Env
want string // expected error from Parse/Check or result from Eval
}{
{"x % 2", nil, "unexpected '%'"},
{"!true", nil, "unexpected '!'"},
{"log(10)", nil, `unknown function "log"`},
{"sqrt(1, 2)", nil, "call to sqrt has 2 args, want 1"},
{"sqrt(A / pi)", Env{"A": 87616, "pi": math.Pi}, "167"},
{"pow(x, 3) + pow(y, 3)", Env{"x": 9, "y": 10}, "1729"},
{"5 / 9 * (F - 32)", Env{"F": -40}, "-40"},
}
for _, test := range tests {
expr, err := Parse(test.input)
if err == nil {
err = expr.Check(map[Var]bool{})
}
if err != nil {
if err.Error() != test.want {
t.Errorf("%s: got %q, want %q", test.input, err, test.want)
}
continue
}
got := fmt.Sprintf("%.6g", expr.Eval(test.env))
if got != test.want {
t.Errorf("%s: %v => %s, want %s",
test.input, test.env, got, test.want)
}
}
}
首先,我們要確保所有的測試都正常通過:
$ go test -v -run=Coverage gopl.io/ch7/eval
=== RUN TestCoverage
--- PASS: TestCoverage (0.00s)
PASS
ok gopl.io/ch7/eval 0.011s
下面這個(gè)命令可以顯示測試覆蓋率工具的使用用法:
$ go tool cover
Usage of 'go tool cover':
Given a coverage profile produced by 'go test':
go test -coverprofile=c.out
Open a web browser displaying annotated source code:
go tool cover -html=c.out
...
go tool
命令運(yùn)行Go工具鏈的底層可執(zhí)行程序。這些底層可執(zhí)行程序放在$GOROOT/pkg/tool/${GOOS}_${GOARCH}目錄。因?yàn)橛?code>go build命令的原因,我們很少直接調(diào)用這些底層工具。
現(xiàn)在我們可以用-coverprofile
標(biāo)志參數(shù)重新運(yùn)行測試:
$ go test -run=Coverage -coverprofile=c.out gopl.io/ch7/eval
ok gopl.io/ch7/eval 0.032s coverage: 68.5% of statements
這個(gè)標(biāo)志參數(shù)通過在測試代碼中插入生成鉤子來統(tǒng)計(jì)覆蓋率數(shù)據(jù)。也就是說,在運(yùn)行每個(gè)測試前,它將待測代碼拷貝一份并做修改,在每個(gè)詞法塊都會(huì)設(shè)置一個(gè)布爾標(biāo)志變量。當(dāng)被修改后的被測試代碼運(yùn)行退出時(shí),將統(tǒng)計(jì)日志數(shù)據(jù)寫入c.out文件,并打印一部分執(zhí)行的語句的一個(gè)總結(jié)。(如果你需要的是摘要,使用go test -cover
。)
如果使用了-covermode=count
標(biāo)志參數(shù),那么將在每個(gè)代碼塊插入一個(gè)計(jì)數(shù)器而不是布爾標(biāo)志量。在統(tǒng)計(jì)結(jié)果中記錄了每個(gè)塊的執(zhí)行次數(shù),這可以用于衡量哪些是被頻繁執(zhí)行的熱點(diǎn)代碼。
為了收集數(shù)據(jù),我們運(yùn)行了測試覆蓋率工具,打印了測試日志,生成一個(gè)HTML報(bào)告,然后在瀏覽器中打開(圖11.3)。
$ go tool cover -html=c.out
綠色的代碼塊被測試覆蓋到了,紅色的則表示沒有被覆蓋到。為了清晰起見,我們將背景紅色文本的背景設(shè)置成了陰影效果。我們可以馬上發(fā)現(xiàn)unary操作的Eval方法并沒有被執(zhí)行到。如果我們針對這部分未被覆蓋的代碼添加下面的測試用例,然后重新運(yùn)行上面的命令,那么我們將會(huì)看到那個(gè)紅色部分的代碼也變成綠色了:
{"-x * -x", eval.Env{"x": 2}, "4"}
不過兩個(gè)panic語句依然是紅色的。這是沒有問題的,因?yàn)檫@兩個(gè)語句并不會(huì)被執(zhí)行到。
實(shí)現(xiàn)100%的測試覆蓋率聽起來很美,但是在具體實(shí)踐中通常是不可行的,也不是值得推薦的做法。因?yàn)槟侵荒苷f明代碼被執(zhí)行過而已,并不意味著代碼就是沒有BUG的;因?yàn)閷τ谶壿嫃?fù)雜的語句需要針對不同的輸入執(zhí)行多次。有一些語句,例如上面的panic語句則永遠(yuǎn)都不會(huì)被執(zhí)行到。另外,還有一些隱晦的錯(cuò)誤在現(xiàn)實(shí)中很少遇到也很難編寫對應(yīng)的測試代碼。測試從本質(zhì)上來說是一個(gè)比較務(wù)實(shí)的工作,編寫測試代碼和編寫應(yīng)用代碼的成本對比是需要考慮的。測試覆蓋率工具可以幫助我們快速識別測試薄弱的地方,但是設(shè)計(jì)好的測試用例和編寫應(yīng)用代碼一樣需要嚴(yán)密的思考。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: