Go的測試框架

Go 語言中自帶了測試框架,在不引入外部包的情況下,也可以編寫完整的測試职辅。這篇文章來看一下Go 提供原生測試能力,及其不足之處聂示,以及補充這些不足的方法域携。

1. 基本測試框架

在 Go 語言中,所有的測試都需要以 _test.go 結(jié)尾鱼喉,這樣go build 不會去編譯 _test.go 結(jié)尾的文件秀鞭,而 go test 會去編譯 _test.go 結(jié)尾的文件。

在編寫測試的時候蒲凶,我們都會用到 testing 這個包气筋,在這個包中拆内,常用的類型有下面這些:

  • testing.T
  • testing.B
  • testing.M

testing.TB 和 testing.PB 平時用的不多旋圆,在這里就不展開說,感興趣的可以自行去搜索麸恍。上面的三類代表了三種不同的測試灵巧,分別是單元測試搀矫、基準測試和 TestMain 測試,對于不同的測試刻肄,在測試方法的入?yún)⒅腥壳颍仨殠线@個類型。

有一類測試例外敏弃,那就是 Example 測試卦羡,這個測試主要用來在文檔中輸出一些測試案例,Example 測試必須以 Example 開頭麦到,方法不需要任何參數(shù)绿饵,同時要指明這個實例的輸出,像下面這樣:

func ExampleTest() {
    fmt.Println("run example test")
    // Output:
    // run example test
}

所有的測試都可以通過 go test 來發(fā)起瓶颠,例如拟赊,在當前包下發(fā)起測試 go test -v ./ ,-v 參數(shù)表示打印測試的過程粹淋,會把測試過程中的標準輸出都打印出來吸祟。

2. 單元測試

單元測試的編寫需要按照一定的規(guī)則來,所有的單元測試都需要以 Test 開頭桃移,后面加上測試的方法名稱屋匕,就像下面這樣:

import (
    "fmt"
    "testing"
)

func TestDemo(t *testing.T) {
    fmt.Println(" run test demo")
}

這就是一個最簡單的單元測試,在實際使用中借杰,一組測試可能會有多個單元測試炒瘟,而且要同時運行,這時我們就需要一個方法將這些測試串聯(lián)起來第步,那就需要用到 TestMain 了疮装,這個方法名稱和簽名是統(tǒng)一的,只能是下面的寫法:

func TestMain(m *testing.M)  {
    fmt.Println("begin test")
    m.Run()
    fmt.Println("end test")
}

TestMain(m *testing.M) 在一個包下只能有一個粘都,測試執(zhí)行的時候廓推,會先執(zhí)行這個方法,然后再去執(zhí)行這個包下的所有 Test 測試和 Example 測試翩隧,基準測試則不會執(zhí)行樊展。

而且這個方法可以用來初始化和回收資源,有些測試在運行之前需要初始化一些配置堆生,連接數(shù)據(jù)庫专缠、釋放數(shù)據(jù)庫連接等操作,就可以在這個測試中完成淑仆。

寫完上面的測試之后涝婉,就可以運行測試了,這樣會從 TestMain 開始蔗怠,運行所有的 Test 和 Example 測試:

$ go test -v ./

但有時候我們也會關(guān)心單元測試的覆蓋率墩弯,只要加上一個參數(shù)就可以看到測試的覆蓋率:

$ go test -cover -v ./

3. 基準測試

基準測試通常用來測試某個程序的性能吩跋,基準測試必須要用 Benchmark 開頭,同時方法的入?yún)⒈仨毷?testing.B渔工,就像下面這樣:

func BenchmarkDemo(b *testing.B) {
    fmt.Println("run benchmark demo")
}

為了更好的說明基準測試的功能锌钮,我用之前測試字符串拼接的基準測試的例子來說明:

func BenchmarkPlus(b *testing.B) {
    str := "this is just a string"

    for i := 0; i < b.N; i++ {
        stringPlus(str)
    }
}

func stringPlus(str string) string {
    s := ""
    for i := 0; i < 10000; i++ {
        s += str
    }
    return s
}

其中 b.N 不是一個固定的值,這個值的大小由框架自己來決定引矩,上面這側(cè)測試的內(nèi)容是對于一個要拼接一萬次字符傳的函數(shù)進行性能測試梁丘,至于這個測試運行多少次,由框架自己決定旺韭。

運行基準測試的命令如下:

$ go test -bench=. -benchmem .

輸出結(jié)果如下:

goos: darwin
goarch: amd64
pkg: zxin.com/zx-demo/string_benchmark
**BenchmarkPlus-12                      12          96586447 ns/op        1086401355 B/op    10057 allocs/op**
PASS
ok      zxin.com/zx-demo/string_benchmark       6.186s

加粗的那行是基準測試的輸出兰吟,每列信息的具體含義如下:

  • 第一列表示基準測試的方法名稱和所用的 GOMAXPROCS 的值
  • 第二列表示這次測試循環(huán)的次數(shù)
  • 第三列表示平均每次測試所用的時間,單位為納秒
  • 第四列表示平均每次運行所分配的內(nèi)存
  • 第五列表示每次運行所分配內(nèi)存的次數(shù)

4. 測試加強

但原生的測試包不夠完美茂翔,比如在單元測試中混蔼,就缺少斷言機制,使得在判斷測試結(jié)果的時候珊燎,非常不方便惭嚣,有一個外部的包可以幫助完善測試的功能。

安裝也很方便:

$ go get github.com/stretchr/testify

這個包從三個方面擴展了 Go 原生測試框架的能力:

  • 斷言

原生測試框架里面缺失斷言功能悔政,在很多場景下都不方便晚吞,testify 提供的斷言功能開箱即用,與原生測試框架完美契合:

func TestAssert(t *testing.T) {
    assert := assert.New(t)

    assert.Equal(123, 123, "they should be equal")

    assert.NotEqual(123, 456, "they should not be equal")

    o := make(map[string]string)
    o["ray"] = "jun"

    if assert.NotNil(o) {
        assert.Equal("jun", o["ray"])
    } else {
        assert.Nil(o)
    }
}
  • Mock 能力

testify 提供了Mock 的能力谋国,可以很好的模擬測試需要的數(shù)據(jù)槽地,對于一些需要復(fù)雜數(shù)據(jù)的測試很有幫助:

type MyMockedObject struct{
    mock.Mock
}

func (m *MyMockedObject) DoSomething(number int) (bool, error) {
    args := m.Called(number)
    return args.Bool(0), args.Error(1)

}

func TestSomething(t *testing.T) {
    testObj := new(MyMockedObject)

    testObj.On("DoSomething", 123).Return(true, nil)

    testMockObj(testObj)

    testObj.AssertExpectations(t)
}

func testMockObj(mcObj *MyMockedObject) {
    fmt.Println(mcObj.DoSomething(123))
}
  • 構(gòu)建更完善的測試

即使有了 TestMain 來初始化配置,但也還是不夠靈活芦瘾,比如在一個包下捌蚊,我需要包含多組測試,而且每組測試的初始化都不一樣近弟,而 testify 提供的 suite 包提供了更加面向?qū)ο蟮臏y試方式缅糟,并且也提供了 setup/teardown 等方法來初始化和回收資源,可以直接使用 go test 進行測試祷愉,不會對現(xiàn)有的測試框架有侵入性修改:

type ExampleTestSuite struct {
    suite.Suite
    VariableThatShouldStartAtFive int
}

func (suite *ExampleTestSuite) SetupTest() {
    fmt.Println("run setup method")
    suite.VariableThatShouldStartAtFive = 5
}

func (suite *ExampleTestSuite) TearDownTest() {
    fmt.Println("run tear down method")
    suite.VariableThatShouldStartAtFive = 0

}

func (suite *ExampleTestSuite) TestExample() {
    assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
}

func TestExampleTestSuite(t *testing.T) {
    suite.Run(t, new(ExampleTestSuite))
}

5. 小結(jié)

雖然 Go 原生測試框架已經(jīng)支持編寫很復(fù)雜的測試窗宦,但很多場景下還不是很方便,這時候就有必要引入新的測試加強包 testify二鳄,這個包基本做到了開箱即用赴涵,而且不會破壞現(xiàn)有的測試流程。

文 / Rayjun

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末订讼,一起剝皮案震驚了整個濱河市髓窜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌躯嫉,老刑警劉巖纱烘,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異祈餐,居然都是意外死亡擂啥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門帆阳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哺壶,“玉大人,你說我怎么就攤上這事蜒谤∩奖觯” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵鳍徽,是天一觀的道長资锰。 經(jīng)常有香客問我,道長阶祭,這世上最難降的妖魔是什么绷杜? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮濒募,結(jié)果婚禮上鞭盟,老公的妹妹穿的比我還像新娘。我一直安慰自己瑰剃,他們只是感情好齿诉,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著晌姚,像睡著了一般粤剧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挥唠,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天俊扳,我揣著相機與錄音,去河邊找鬼猛遍。 笑死馋记,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的懊烤。 我是一名探鬼主播梯醒,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼腌紧!你這毒婦竟也來了茸习?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤壁肋,失蹤者是張志新(化名)和其女友劉穎号胚,沒想到半個月后籽慢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡猫胁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年箱亿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弃秆。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡届惋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出菠赚,到底是詐尸還是另有隱情脑豹,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布衡查,位于F島的核電站瘩欺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拌牲。R本人自食惡果不足惜击碗,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望们拙。 院中可真熱鬧稍途,春花似錦、人聲如沸砚婆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽装盯。三九已至坷虑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間埂奈,已是汗流浹背迄损。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留账磺,地道東北人芹敌。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像垮抗,于是被迫代替她去往敵國和親氏捞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 參考《Go語言圣經(jīng)》P395 一冒版、概述 go test命令是一個按照一定的約定和組織的測試代碼的驅(qū)動程序液茎。在包目錄...
    合肥黑閱讀 875評論 0 4
  • go test命令,相信大家都不陌生,常見的情況會使用這個命令做單測試捆等、基準測試和http測試滞造。go test還是...
    筆名輝哥閱讀 2,779評論 0 52
  • 在*_test.go文件中,有三種類型的函數(shù):測試函數(shù)栋烤、基準測試(benchmark)函數(shù)谒养、示例函數(shù)。一個測試函數(shù)...
    一斗閱讀 1,680評論 0 0
  • http://c.biancheng.net/view/124.html[http://c.biancheng.n...
    JunChow520閱讀 373評論 0 1
  • go語言內(nèi)置的測試框架能夠完成基本的功能測試班缎,基準測試蝴光,和樣本測試她渴。 測試框架 go語言測試單元以包為單位組織达址,包...
    CodingCode閱讀 3,037評論 0 0