golang 打樁,mock 數(shù)據(jù)怎么玩?
工作中,很多公司都要求效能,要求自動化測試
實(shí)際落地的過程中發(fā)現(xiàn),要做單元測試,自動化測試,可能當(dāng)前這個服務(wù)會依賴其他服務(wù)的數(shù)據(jù),接口等等
那么單測或者自動化的過程中,就可能會由于其他服務(wù)的原因或者環(huán)境因素導(dǎo)致測試失敗,或者阻塞測試
這是一個問題,必須得解決,我們可以采用 golang 自帶的 mock 工具來完成,可以在一些必要的地方進(jìn)行數(shù)據(jù)打樁,mock 數(shù)據(jù)
gomock 是什么?
是官方提供的 一個 mock 數(shù)據(jù)的 框架
官方還提供了 mockgen 工具用來幫助 我們 生成測試代碼
github 上項(xiàng)目地址是:https://github.com/golang/mock
官方是這樣介紹 gomock 的:
gomock 是一個用于Go 編程語言的 mocking 框架。它與 Go 的內(nèi)置測試包集成得很好,但也可以在其他環(huán)境中使用。
如何使用 gomock?
使用 gomock 也是非常簡單的,先 go get 對應(yīng)的 工具 ?gomock ?和 ?mockgen
go?get?-u?github.com/golang/mock/gomock
go?get?-u?github.com/golang/mock/mockgen
可以寫一個 demo 來進(jìn)行實(shí)踐
目錄結(jié)構(gòu)是這樣的
gomock_test
├──?go.mod
├──?go.sum
├──?main.go
└──?myfunc
????├──?mock_myfunc.go
????├──?myfunc.go
????├──?myuser.go
????└──?myuser_test.go

mock_myfunc.go 是使用 mockgen 工具生成的
myfunc.go 主要是用于模擬調(diào)用的底層實(shí)現(xiàn)
myuser.go 主要是去調(diào)用 ?myfunc.go 里面的接口
myuser_test.go 是 對應(yīng)的單測文件
myfunc.go
編寫一個 接口,里面有一個
GetInfo() string
方法,模擬獲取信息
package?myfunc
type?MyFunc?interface?{
?GetInfo()?string
}
myuser.go
調(diào)用 myfunc.go 中的方法,調(diào)用接口獲取信息
package?myfunc
func?getUser(m?MyFunc)?string?{
?user?:=?m.GetInfo()
?return?user
}
mock 文件的生成
mock_myfunc.go
這個文件不是我們自己寫的,是通過 mockgen 工具生成的 ,生成方式如下:
在 myfunc.go 的同級目錄下執(zhí)行如下語句,填入 source 源文件 和 目標(biāo)文件即可生成新的 mock 文件
mockgen?-source=myfunc.go?-destination=mock_myfunc.go
我們可以看一下 mockgen 的幫助文檔,還有其他的參數(shù)供我們使用
?mockgen
mockgen?has?two?modes?of?operation:?source?and?reflect.
Source?mode?generates?mock?interfaces?from?a?source?file.
It?is?enabled?by?using?the?-source?flag.?Other?flags?that
may?be?useful?in?this?mode?are?-imports?and?-aux_files.
Example:
????????mockgen?-source=foo.go?[other?options]
Reflect?mode?generates?mock?interfaces?by?building?a?program
that?uses?reflection?to?understand?interfaces.?It?is?enabled
by?passing?two?non-flag?arguments:?an?import?path,?and?a
comma-separated?list?of?symbols.
Example:
????????mockgen?database/sql/driver?Conn,Driver
??-aux_files?string
????????(source?mode)?Comma-separated?pkg=path?pairs?of?auxiliary?Go?source?files.
??-build_flags?string
????????(reflect?mode)?Additional?flags?for?go?build.
??-copyright_file?string
????????Copyright?file?used?to?add?copyright?header
??-debug_parser
????????Print?out?parser?results?only.
??-destination?string
????????Output?file;?defaults?to?stdout.
??-exec_only?string
????????(reflect?mode)?If?set,?execute?this?reflection?program.
??-imports?string
????????(source?mode)?Comma-separated?name=path?pairs?of?explicit?imports?to?use.
??-mock_names?string
????????Comma-separated?interfaceName=mockName?pairs?of?explicit?mock?names?to?use.?Mock?names?default?to?'Mock'+?interfaceName?suffix.
??-package?string
????????Package?of?the?generated?code;?defaults?to?the?package?of?the?input?with?a?'mock_'?prefix.
??-prog_only
????????(reflect?mode)?Only?generate?the?reflection?program;?write?it?to?stdout?and?exit.
??-self_package?string
????????The?full?package?import?path?for?the?generated?code.?The?purpose?of?this?flag?is?to?prevent?import?cycles?in?the?generated?code?by?trying?to?include?its?own?package.?This?can?happen?if?the?mock's?package?is?set?to?one?of?its?inputs?(usually?the?main?one)?and?the?output?is?stdio?so?mockgen?cannot?detect?the?final?output?package.?Setting?this?flag?will?then?tell?mockgen?which?import?to?exclude.
??-source?string
????????(source?mode)?Input?Go?source?file;?enables?source?mode.
??-version
????????Print?version.
??-write_package_comment
????????Writes?package?documentation?comment?(godoc)?if?true.?(default?true)
2021/10/30?16:43:25?Expected?exactly?two?arguments
一般用的比較多的就是
-source
源文件-destination
目標(biāo)文件-imports
?依賴的需要 import 的包-build_flags
傳遞給build工具的參數(shù)-aux_files
接口文件不止一個文件時附加文件-package
設(shè)置 mock 文件的包名,不設(shè)置的話,mock 文件的包名默認(rèn)是mock_輸入文件的包名
通過上述指令生成的 mock 文件如下:

NewMockMyFunc
創(chuàng)建一個新的 mock 實(shí)例
EXPECT
允許調(diào)用者指示預(yù)期用途的對象
GetInfo
mock 的基礎(chǔ)方法,也就是我們需要 mock 的方法
具體的如何使用
myuser_test.go
myuser.go 對應(yīng)的單測文件 , 使用了 mock 的方式
package?myfunc
import?(
?"fmt"
?"testing"
?gomock?"github.com/golang/mock/gomock"
)
func?Test_getUser(t?*testing.T)?{
?mockCtl?:=?gomock.NewController(t)
?mockMyFunc?:=?NewMockMyFunc(mockCtl)
?mockMyFunc.EXPECT().GetInfo().Return("xiaomotong")
?v?:=?getUser(mockMyFunc)
?if?v?==?"xiaomotong"?{
??fmt.Println("get?user?right!")
?}?else?{
??t.Error("get?error?user")
?}
}
看到上述單測文件,可以還不是特別明白區(qū)別,我們來看看不用 mock 的時候,我們會是如何去寫單測呢
package?myfunc
import?(
?"fmt"
?"testing"
?gomock?"github.com/golang/mock/gomock"
)
func?Test_getUser(t?*testing.T)?{
????m?:=?myfunc.CreateMyFunc()?//?也就是說需要自己創(chuàng)建一個對象
?v?:=?getUser(m)
?if?v?==?"xiaomotong"?{
??fmt.Println("get?user?right!")
?}?else?{
??t.Error("get?error?user")
?}
}
m := myfunc.CreateMyFunc()
看到上述這一句話,是創(chuàng)建對應(yīng)的對象,再將該對象作為參數(shù)傳入到 ?getUser 函數(shù)中,正常情況下這樣做單測沒有問題
但是如果這個時候創(chuàng)建 MyFunc 對象由于對外部還有依賴導(dǎo)致還沒有編碼好,可是也不能阻塞我們的單元測試
這個時候使用最上面的 mock 方案就顯得尤為重要,可以使用 mock 的方式,mock 一個 MyFunc 對象,并設(shè)置好返回值即可完成,如:
mockCtl?:=?gomock.NewController(t)
mockMyFunc?:=?NewMockMyFunc(mockCtl)
mockMyFunc.EXPECT().GetInfo().Return("xiaomotong")
執(zhí)行上述代碼結(jié)果如下:
?go?test
get?user?right!
PASS
ok??????mygomock/myfunc?0.427s
感興趣的朋友可以使用起來,用的多了就會更加熟悉
使用 gomock 的好處?
gomock ?實(shí)現(xiàn)了較為完整的基于 interface 的 Mock 功能,能夠與 Golang 內(nèi)置的 testing包良好集成,也能用于其它的測試環(huán)境中
學(xué)習(xí)成本低,很快就能上手
工具需要用起來,才能發(fā)揮他的價值,需要的可以用起來吧
歡迎點(diǎn)贊,關(guān)注,收藏
朋友們,你的支持和鼓勵,是我堅(jiān)持分享,提高質(zhì)量的動力

好了,本次就到這里
常見技術(shù)是開放的,我們的心態(tài),更應(yīng)是開放的。擁抱變化,向陽而生,努力向前行。
我是阿兵云原生,歡迎點(diǎn)贊關(guān)注收藏,下次見~