在計算機編程中冻记,單元測試Unit Testing
又稱為模塊測試,是針對程序模塊來進行正確性檢驗的測試工作撼玄。
在Go語言中有幾種方法可以用于單元測試戈咳,基礎測試basic test
是指只使用一組參數(shù)和結果來測試一段代碼猜憎;表組測試table test
是指使用多組參數(shù)和結果測試一段代碼尉尾。此外蚂子,還可以使用一些方法來模仿mock
測試代碼需要使用到的外部資源本缠,例如數(shù)據(jù)庫或者網(wǎng)絡服務器斥扛。
單元測試
testing
是Go語言的一個包,它將提供自動化測試的功能丹锹,通過go test
命令能夠自動執(zhí)行如下形式的函數(shù):
func TestXxx(*testing.T)
其中Xxx
可以是任何字母稀颁、數(shù)字、字符串楣黍,但是第一個字母一定不能是小寫字母匾灶。在這些函數(shù)中,使用Error
租漂,Fail
或相關方法來返回失敗信號阶女。
要編寫一個新的測試模塊,需要創(chuàng)建一個名稱以_test.go
為結尾的文件哩治,該文件包含TestXxx
函數(shù)秃踩,最后將該文件放在與被測試的包相同的包目錄中。
第一個單元測試
我們來看一個簡單的例子:
package main
import "fmt"
func main() {
fmt.Println(Age(-7))
}
func Age(n int) int {
if n > 0 {
return n
}
n = 0
return n
}
我們在來寫一下測試文件业筏,注意名稱的定義吞瞪,一定要記得在文件名稱后面加_test.go
:
package main
import "testing"
func TestFib(t *testing.T) {
var (
input = -100
expected = 0
)
actual := Age(input)
if actual != expected {
t.Errorf("Fib(%d) = %d, expected is %d.", input, actual, expected)
}
}
當我們運行之后得到的結果為ok
時,那就表明我們的測試是成功的驾孔,函數(shù)也是沒有問題的芍秆。
這就是基礎測試,我們接下來再看一下表組測試翠勉,它是可以提供多組數(shù)據(jù)的測試方式妖啥。
表組測試
我們通過一個判斷一個數(shù)字是否為素數(shù)的例子來看一下表組測試:
package main
import "fmt"
func main() {
fmt.Println(isPrime(25))
}
func isPrime(value int) bool {
if value <= 3 {
return value >= 2
}
if value%2 == 0 || value%3 == 0 {
return false
}
for i := 5; i <= value; i += 6 {
if value%i == 0 || value%(i+2) == 0 {
return false
}
}
return true
}
基于以上的程序,我們的測試文件可以這樣來寫:
package main
import "testing"
func TestPrime(t *testing.T) {
var primeTests = []struct {
input int
expected bool
}{
{1, false},
{2, true},
{3, true},
{4, false},
{5, true},
{6, false},
{7, false},
}
for _, tt := range primeTests {
actual := isPrime(tt.input)
if actual != tt.expected {
t.Errorf("%d %v %v", tt.input, actual, tt.expected)
}
}
}
在每一次的測試當中对碌,測試函數(shù)執(zhí)行結束返回荆虱,或者測試函數(shù)調(diào)用FailNow
,Fatal
,Fatalf
怀读,SkipNow
诉位,Skip
和Skipf
中的任意一個時,這次測試即宣告結束菜枷。
Fail
記錄失敗信息苍糠,然后繼續(xù)執(zhí)行后續(xù)用例。
FailNow
記錄失敗信息啤誊,所有測試中斷岳瞭。
SkipNow
不會記錄失敗的用例信息,然后終止測試蚊锹。
Skip
記錄失敗信息瞳筏,中斷后續(xù)測試。
Skipf
相比前者多了一個格式化輸出牡昆。
模擬測試
針對模擬網(wǎng)絡訪問姚炕,標準庫提供了一個httptest
包,可以模擬HTTP
的網(wǎng)絡調(diào)用丢烘,下面我們還是通過例子來看一下:
import (
"encoding/json"
"net/http"
)
func Routes() {
http.HandleFunc("/sendjson", SendJSON)
}
func SendJSON(rw http.ResponseWriter, r *http.Request) {
u := struct {
Name string
}{
Name: "Alice",
}
rw.Header().Set("Content-Type", "application/json")
rw.WriteHeader(http.StatusOK)
json.NewEncoder(rw).Encode(u)
}
現(xiàn)在我們對這API
服務進行測試:
func init() {
request.Routes()
}
func TestSendJSON(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, "/sendjson", nil)
if err != nil {
t.Fatal("Creating request failed.")
}
rw := httptest.NewRecorder()
http.DefaultServeMux.ServeHTTP(rw, req)
log.Println("code: ", rw.Code)
log.Println("body: ", rw.Body.String())
}
測試覆蓋率
盡可能模擬更多的場景來測試代碼的不同情況钻心,但是有時候的確也有忘記測試的代碼,這時候就需要涉及到測試覆蓋率的概念來進行參考了铅协。
我們在對測試覆蓋率進行測試的時候,需要在執(zhí)行go test
的時候多添加一個參數(shù)-coverprofile
摊沉,完整的命令就是:
go test -v -coverprofile=c.out
-coverprofile
是指定生成的覆蓋率文件狐史,在我們的例子中是c.out
。
$ go test -v -coverprofile=c.out
=== RUN TestPrime
--- PASS: TestPrime (0.00s)
PASS
coverage: 66.7% of statements
ok _/home/hdc/goProgramming/testing1 0.001s
我們可以看到覆蓋率為66.7%
说墨。說明并沒有完全覆蓋骏全,那么我們該如何查看還有哪些代碼沒有被測試到呢?這時就需要使用測試覆蓋率文件c.out
了尼斧。生成報告使用的工具是:
go tool cover -html=c.out -o=tag.html
這樣就可以生成一個名字為tag.html
的HTML
格式的測試覆蓋率報告了姜贡。但一定要注意文件存放的路徑,因為路徑如果不滿足條件的話是無法生成的棺棵。