天天看點

Go的自動化測試

自動化測試,是在預設條件下運作系統或應用程式,評估運作結果,預先條件包括正常條件和異常條件。Go本身提供了一套輕量級的測試架構,相對低級,但不過還是有效的。它依賴一個

go test

測試指令,和一組按照約定方式編寫的測試函數,符合規則的測試代碼會在運作測試時被自動識别并執行。但是這些檔案是不會被普通的Go編譯器編譯,是以當将應用部署到生産環境時,它們不會被部署。

測試規則

檔案命名與放置規則

  1. 測試源檔案名應是

    _test

    結尾的go檔案,例如:

    add_test.go

  2. 測試代碼和業務代碼是分開的,但兩者應該位于同一包下;
  3. 測試源檔案所在的包應該位于

    $GOPATH/src

    目錄下;
  4. 測試檔案必須導入

    "testing"

    包;
在Go中,凡是以

_

或者

.

符号作為檔案名的首字母時,該檔案都會被建構工具忽略掉。以

_test.go

結尾的測試檔案,将會被編譯為單獨的包,然後将其連結到主測試二進制檔案。

go工具還會忽略名為

testdata

的目錄,使其可以儲存測試所需的輔助資料。

測試函數命名規則

測試檔案包括三種函數:測試函數、基準測試函數和例子函數。測試函數用于測試程式的一些邏輯行為是否正确的函數,在命名時應該以

Test

為字首,例如

func TestAbcde(t *testing.T)

。基準測試用于衡量一些函數的性能,是以

Benchmark

為函數名字首的函數,例如

func BenchmarkAbcde(b *testing.B)

。例子函數用來說明函數(變量,常量等)如何使用,以

Example

為函數名字首的函數,例如:

func ExamplePrintln(){
    Println("The output of\nthis example.")
    //Output: The output of
    //this example.
}
           

go test指令

go test

指令會自動測試導入路徑命名的包并緩存測試結果,根據測試的結果和預先設定的是否比對,會傳回

PASS

FAIL

。它的用法:

go test [build/test參數] [包名] [build/test參數 &測試編譯參數]

,它有兩種不同的模式:本地目錄模式和包清單模式。僅在包清單模式下,

go test

會緩存成功的包測試結果以避免不必要的重複運作已經通過的測試。當測試的結果可以從緩存中恢複,

go test

将會重新顯示以前的輸出而不是再次運作測試二進制檔案。例如你需要對

math

目錄下的

a_test.go

檔案和

b_test.go

檔案做基準測試,那麼就可以執行

go test -bench . math a_test.go b_test.go

本地目錄模式

這種模式是在沒有包參數的情況下,調用

go test

時發生,例如:

go test

go test -v

。在此模式下,

go test

會編譯目前目錄中的包和測試檔案,然後運作生成的二進制檔案。這種模式下,緩存是被禁用的。測試完成後,

go test

會列印結果摘要,逐行顯示測試狀态,包名稱和已用的時間。

包清單模式

在調用

go test

時傳入顯式包參數,例如:

go test math

go test ./...

,或者

go test .

等。在此模式下,

go test

會編譯和測試指令行中列出的每一個包。如果一個包測試通過,

go test

隻列印成功摘要,但如果測試失敗,則會列印完整的資訊。如果你傳入參數

-bench

-v

标志,那麼

go test

将會列印完整的輸出,即使是通過測試的包。第一個參數是當有基準測試函數時使用,第二種就是平常的測試函數就行。

顯式禁用緩存的慣用方法是使用

-count=1

go test參數

-bench [正規表達式]:僅運作與正規表達式比對的基準,預設情況下,不運作基準測試。如果要運作所有的基準測試函數,使用

-bench .

或者

-bench=.

-benchtime [時間]:表示運作多長時間的基準測試,預設值為

1

秒。例如:

-benchtime 1h30s

-count n:運作每個測試和基準多少次,預設為

1

,如果設定了

-cpu

,則為每個

GOMAXPROCS

運作

n

次,例子函數總是運作一次。

-cover:啟用覆寫率分析,由于覆寫率通過在編譯之前注釋源代碼來工作,是以啟用覆寫率的編譯和測試失敗,可能會出現報告與原始檔案行号不對應的情況。

-covermode set,count,atomic:為正在測試的包設定覆寫率分析模式,除非啟用

-race

,否則預設為

set

,在這種情況下它是原子的。

  • set

    ,布爾值,表示這句聲明是否有效
  • count

    ,int類型,這句聲明運作多少次
  • atomic

    ,int類型,在多線程測試中使用,原子操作。

-coverpkg pattern1,pattern2,pattern3:将每個測試中的覆寫率分析應用于模式比對的包,預設情況下,每個測試僅分析正在測試的包。

-cpu 1,2,4:指定應為其執行測試或基準測試的

GOMAXPROCS

的值的清單,預設值是

GOMAXPROCS

的目前值。

-failfast:表示在第一次測試失敗後不要開始新的測試。

-list [正規表達式]:隻是列出與正規表達式比對的測試、基準測試或例子,不會運作任何測試。

-parallel n:表示運作的最大測試數,預設情況下,它設定為

GOMAXPROCS

的值。

-run [正規表達式]:僅僅運作與正規表達式比對的那些測試和例子,比對時可能父項也會運作,例如:

-run = X/Y

,比對運作所有與X比對的測試的結果,即使沒有比對到子測試。

-timeout [時間]:如果測試檔案運作的時間超過設定的時間,就會出現

panic

。如果設定的時間為

,則表示

timeout

不可用。預設是

10

分鐘。

-v:會列印詳細的測試結果,即使是在測試成功的情況。

testing包

提供有關Go自動化測試的支援,它與

go test

指令一起使用。

// TB 是類型T和B的接口.
type TB interface {
	Error(args ...interface{})  //Fail+Log
	Errorf(format string, args ...interface{})
	Fail()          //标記失敗,但繼續執行該測試函數
	FailNow()       //失敗,立即停止目前測試函數
	Failed() bool
	Fatal(args ...interface{})  //FailNow+Log
	Fatalf(format string, args ...interface{})
	Log(args ...interface{})    //輸出資訊,僅在失敗或-v參數時輸出
	Logf(format string, args ...interface{})
	Name() string
	Skip(args ...interface{})
	SkipNow()                   //跳過目前測試函數
	Skipf(format string, args ...interface{})
	Skipped() bool
	Helper()
	private()
}
           

例子

1.在你的

$GOPATH/src

目錄下,建立一個目錄,例如:

mytest

檔案夾。

2.建立一個

main

檔案,寫入業務代碼。例如下面将0加到n的函數進行測試:

package main

func addNum(n int) (result int) {
	for i := 0; i <= n; i++ {
		result = result + i
	}
	return
}
           

3.建立測試檔案,檔案名以

_test.go

結尾,例如:

main_test.go

package main

import (
	"testing"
)

func TestAddNum(t *testing.T) {
	if addNum(100) != 5050 {
		t.Fatal("addNum error!")
	}
}
           

4.在指令行裡,直接輸入

go test

,運作目前包的所有測試檔案。或者運作

go test mytest

,隻測試

mytest

包的測試檔案。

本地目錄方式運作時的傳回結果:

PASS
ok      mytest  0.421s
           

包清單模式運作的傳回結果:

ok mytest 0.138s

5.在

main.go

檔案中,添加基準測試函數。

package main

import (
	"testing"
)

func TestAddNum(t *testing.T) {
	if addNum(100) != 5050 {
		t.Fatal("addNum error!")
	}
}

func BenchmarkAddNum(b *testing.B) {
	for i := 0; i < b.N; i++ {
		if addNum(100) != 5050 {
			b.Fatal("addNum")
		}
	}
}
           

6.運作基準測試,

go test -bench .

,預設情況下,

go test

不會執行性能測試函數。性能測試需要運作足夠多的次數才能計算單次執行平均時間。

goos: windows
goarch: amd64
pkg: mytest
BenchmarkAddNum-4       10000000               187 ns/op
PASS
ok      mytest  2.522s
           

報告顯示執行addNum函數花費的平均時間是2.522s,執行了10000000次,每282ns的速度運作一次循環。因為基準測試驅動器并不知道每個基準測試函數運作所花的時間,是以它會在真正運作基準測試前先試用較小的N來運作測試,估算基準測試函數所需要的時間,然後推斷一個較大的時間保證穩定的測量結果。

循環在基準測試函數内實作,而不是放在基準測試架構内實作,這樣可以讓每個基準測試函數有機會在循環啟動前執行初始化代碼,這樣并不會顯著影響每次疊代的平均運作時間。

如果在運作基準測試之前需要一些費事的設定,則可能會重置計時器。例如:

func BenchmarkAddNum(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if addNum(100) != 5050 {
			b.Fatal("addNum")
		}
	}
}
           

參考文章:

  1. Package testing
  2. Command go