詳解Golang中g(shù)omock的使用場景和方法
可惜的是 2023年6月官方停止維護了,但是我們依舊可以使用 uber 團隊維護的分支,gomock 依舊是 go 測試中非常值得學(xué)習(xí)的框架。
介紹
gomock 主要的作用是幫助我們模擬復(fù)雜的函數(shù)和對象,例如外部接口/RPC調(diào)用/數(shù)據(jù)庫連接/文件操作,當然,除了 gomock 社區(qū)還有非常多的其他 mock 框架,例如針對數(shù)據(jù)庫這類的復(fù)雜對象進行 mock ,總的來說mock類型的框架目的就是 為了幫助我們模擬復(fù)雜對象的行為,減少我們手動創(chuàng)建這些模擬對象的額外負擔。
配合 gomock 的還有 mockgen 工具,用來輔助我們生成測試代碼,下面介紹如何安裝和使用它們。
安裝
go get -u github.com/golang/mock/gomock go install github.com/golang/mock/mockgen@v1.6.0
安裝完成后終端驗證 mockgen -version
輸出如下:
mockgen -version v1.6.0
mockgen
安裝完成 mockgen 之后,我們可以簡單了解一下它支持那些操作,以及常用的 option,終端輸入 mockgen
了解相關(guān)的信息:
- mockgen 有兩種運行模式:源模式和反射模式
- 源模式是從源文件中生成 mock 接口,使用 -source 來啟用源模式,除此之外還可以配合使用 -imports 和 -aux_files 等參數(shù)。
- 反射模式通過反射來實現(xiàn)一個接口的模擬,需要傳入導(dǎo)入的路徑和一個逗號分割的參數(shù)列表來創(chuàng)建。
以下是 Gomock 的一般使用步驟:
定義接口:首先,您需要定義一個要模擬的接口。
生成模擬代碼:在包含接口定義的包目錄下,運行 gomock 命令來生成模擬代碼。例如:gomock --package=your_package_name
- --package :指定生成的模擬代碼所在的包名。
- --write_package_comment :在生成的代碼中添加包注釋。
- --self_package :指定生成的模擬對象屬于當前包。
- --output :指定生成模擬代碼的輸出文件路徑。
代碼示例
首先聲明接口,在 main.go 文件中創(chuàng)建一個接口,同時借助 go:generate 來幫助我們自動生成相關(guān)的代碼:
//go:generate mockgen -destination=./mock_human.go -package=main -source=main.go type Human interface { Say(str string) string }
執(zhí)行:go generate ./...
之后,會在當前文件夾下生成一個 mock_human.go 文件,下面我們編寫測試文件 main_test.go,添加測試用例。
func Test_mock_human(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) mockHuman.EXPECT().Say("test").Return("test") if mockHuman.Say("test") != "test" { t.Errorf("say fail") } }
執(zhí)行測試,輸出如下:
=== RUN Test_mock_human --- PASS: Test_mock_human (0.00s) PASS
上面的例子我們僅僅是做演示,方法中用到的方法下面做一下說明,同時將其他常用的方法也做一下說明。
func Test_mock_human(t *testing.T) { t.Run("指定方法的參數(shù)和返回值", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) // 設(shè)置方法的入?yún)⒑头祷刂? mockHuman.EXPECT().Say("test").Return("test result") if mockHuman.Say("test") != "test result" { t.Errorf("say fail") } }) t.Run("EXPECT斷言方法被調(diào)用", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) // EXPECT() 方法會斷言方法被調(diào)用一次 mockHuman.EXPECT().Say("run 1 times").Return("run 1 times result") // 如果不使用指定參數(shù)調(diào)用方法,會報錯:aborting test due to missing call(s) if ret := mockHuman.Say("run 1 times"); ret != "run 1 times result" { t.Errorf("say fail:%v", ret) } }) t.Run("指定方法的調(diào)用次數(shù)", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) mockHuman.EXPECT().Say("run 2 times").Return("run 2 times result").Times(2) if ret := mockHuman.Say("run 2 times"); ret != "run 2 times result" { t.Errorf("say fail:%v", ret) } if ret := mockHuman.Say("run 2 times"); ret != "run 2 times result" { t.Errorf("say fail:%v", ret) } }) t.Run("不指定方法的調(diào)用次數(shù)", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) // 可以調(diào)用mock方法任意此處(包含0次) mockHuman.EXPECT().Say("run any times").Return("run any times result").AnyTimes() }) t.Run("指定方法的最少調(diào)用次數(shù)", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) // 至少調(diào)用mock方法若干次 mockHuman.EXPECT().Say("min times").Return("min times result").MinTimes(1) if ret := mockHuman.Say("min times"); ret != "min times result" { t.Errorf("say fail:%v", ret) } }) t.Run("指定方法的最多調(diào)用次數(shù)", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) // 至多調(diào)用mock方法若干次 mockHuman.EXPECT().Say("max times").Return("max times result").MaxTimes(1) if ret := mockHuman.Say("max times"); ret != "max times result" { t.Errorf("say fail:%v", ret) } }) t.Run("指定方法的實現(xiàn)細節(jié)-DoAndReturn", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) // 覆蓋方法的實現(xiàn),自定義方法的mock實現(xiàn) mockHuman.EXPECT().Say("do and return").DoAndReturn(func(str string) string { return "custom return content" }) if ret := mockHuman.Say("do and return"); ret != "custom return content" { t.Errorf("say fail:%v", ret) } }) t.Run("指定方法的實現(xiàn)細節(jié)-Do", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) // 覆蓋方法的實現(xiàn),自定義方法的mock實現(xiàn) mockHuman.EXPECT().Say("do and return").Do(func(s string) { fmt.Printf("重新實現(xiàn)方法,%s", s) }).Return("custom return content") if ret := mockHuman.Say("do and return"); ret != "custom return content" { t.Errorf("say fail:%v", ret) } }) t.Run("不指定具體參數(shù)", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) // 這里表示無論傳入什么字符串 mockHuman.EXPECT().Say(gomock.Any()).Return("any arg is okay2").AnyTimes() if ret := mockHuman.Say("1"); ret != "any arg is okay2" { t.Errorf("say fail:%v", ret) } if ret := mockHuman.Say("2"); ret != "any arg is okay2" { t.Errorf("say fail:%v", ret) } }) t.Run("指定傳入?yún)?shù)", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) // 這里表示參數(shù)必須傳入什么字符串 mockHuman.EXPECT().Say(gomock.Eq("valid string")).Return("valid result") if ret := mockHuman.Say("valid string"); ret != "valid result" { t.Errorf("say fail:%v", ret) } }) t.Run("指定傳入?yún)?shù)不是某一個具體值", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) // 這里表示傳入的參數(shù)不能是 valid string mockHuman.EXPECT().Say(gomock.Not("valid string")).Return("valid result") if ret := mockHuman.Say("invalid string"); ret != "valid result" { t.Errorf("say fail:%v", ret) } }) t.Run("指定傳入?yún)?shù)是nil", func(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockHuman := NewMockHuman(ctrl) // 這里表示傳入的參數(shù)必須是nil mockHuman.EXPECT().Say2(gomock.Nil()).Return("valid result") if ret := mockHuman.Say2(nil); ret != "valid result" { t.Errorf("say fail:%v", ret) } }) }
涉及到的方法和方法的作用:
- EXPECT:用于設(shè)置對模擬對象方法調(diào)用的期望,可以定義模擬方法在被調(diào)用時的各種期望行為,例如期望的調(diào)用次數(shù)、傳入的參數(shù)、返回的值以及可能的副作用操作
- Times:用于指定期望的方法調(diào)用次數(shù)。
- AnyTimes:表示模擬的方法可以被調(diào)用任意次數(shù),包括 0 次。
- MinTimes:指定模擬方法被調(diào)用的最小次數(shù)。
- MaxTimes:指定模擬方法被調(diào)用的最大次數(shù)。
- Do:模擬方法被調(diào)用時執(zhí)行一個自定義的函數(shù),這個函數(shù)可以包含一些額外的邏輯或副作用。
- DoAndReturn:與 Do 類似,但同時還能指定返回值。
- Eq:用于驗證傳入模擬方法的參數(shù)是否與指定的值相等。
- Not: 常與其他匹配器結(jié)合使用,用于表示相反的條件。
- Nil:用于檢查某個值是否為 nil 。
以上就是 gomock 的使用方法和一些概念。
到此這篇關(guān)于詳解Golang中g(shù)omock的使用場景和方法的文章就介紹到這了,更多相關(guān)Go gomock內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
以alpine作為基礎(chǔ)鏡像構(gòu)建Golang可執(zhí)行程序操作
這篇文章主要介紹了以alpine作為基礎(chǔ)鏡像構(gòu)建Golang可執(zhí)行程序操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12golang中切片copy復(fù)制和等號復(fù)制的區(qū)別介紹
這篇文章主要介紹了golang中切片copy復(fù)制和等號復(fù)制的區(qū)別,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04