原文地址:使用 Gomock 進行單元測試html
在實際項目中,須要進行單元測試的時候。卻每每發現有一大堆依賴項。這時候就是 Gomock 大顯身手的時候了git
Gomock 是 Go 語言的一個 mock 框架,官方的那種 🤪github
$ go get -u github.com/golang/mock/gomock $ go install github.com/golang/mock/mockgen
一、 第一步:咱們將安裝 gomock 第三方庫和 mock 代碼的生成工具 mockgen。然後者能夠大大的節省咱們的工做量。只須要了解其使用方式就能夠golang
二、 第二步:輸入 mockgen
驗證代碼生成工具是否安裝正確。若沒法正常響應,請檢查 bin
目錄下是否包含該二進制文件sql
在 mockgen
命令中,支持兩種生成模式:安全
一、 source:從源文件生成 mock 接口(經過 -source 啓用)框架
mockgen -source=foo.go [other options]
二、 reflect:經過使用反射程序來生成 mock 接口。它經過傳遞兩個非標誌參數來啓用:導入路徑和逗號分隔的接口列表函數
mockgen database/sql/driver Conn,Driver
從本質上來說,兩種方式生成的 mock 代碼並無什麼區別。所以選擇合適的就能夠了工具
在本文將模擬一個簡單 Demo 來編寫測試用例,熟悉總體的測試流程單元測試
mockgen
命令對所需 mock 的 interface 生成 mock 文件├── mock ├── person │ └── male.go └── user ├── user.go └── user_test.go
打開 person/male.go 文件,寫入如下內容:
package person type Male interface { Get(id int64) error }
打開 user/user.go 文件,寫入如下內容:
package user import "github.com/EDDYCJY/mockd/person" type User struct { Person person.Male } func NewUser(p person.Male) *User { return &User{Person: p} } func (u *User) GetUserInfo(id int64) error { return u.Person.Get(id) }
回到 mockd/
的根目錄下,執行如下命令
$ mockgen -source=./person/male.go -destination=./mock/male_mock.go -package=mock
在執行完畢後,能夠發現 mock/
目錄下多出了 male_mock.go 文件,這就是 mock 文件。那麼命令中的指令又分別有什麼用呢?以下:
mock_
前綴加上文件名(如本文的包名會爲 mock_person)想了解更多的指令符,可參見 官方文檔
// Code generated by MockGen. DO NOT EDIT. // Source: ./person/male.go // Package mock is a generated GoMock package. package mock import ( gomock "github.com/golang/mock/gomock" reflect "reflect" ) // MockMale is a mock of Male interface type MockMale struct { ctrl *gomock.Controller recorder *MockMaleMockRecorder } // MockMaleMockRecorder is the mock recorder for MockMale type MockMaleMockRecorder struct { mock *MockMale } // NewMockMale creates a new mock instance func NewMockMale(ctrl *gomock.Controller) *MockMale { mock := &MockMale{ctrl: ctrl} mock.recorder = &MockMaleMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use func (m *MockMale) EXPECT() *MockMaleMockRecorder { return m.recorder } // Get mocks base method func (m *MockMale) Get(id int64) error { ret := m.ctrl.Call(m, "Get", id) ret0, _ := ret[0].(error) return ret0 } // Get indicates an expected call of Get func (mr *MockMaleMockRecorder) Get(id interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockMale)(nil).Get), id) }
打開 user/user_test.go 文件,寫入如下內容:
package user import ( "testing" "github.com/EDDYCJY/mockd/mock" "github.com/golang/mock/gomock" ) func TestUser_GetUserInfo(t *testing.T) { ctl := gomock.NewController(t) defer ctl.Finish() var id int64 = 1 mockMale := mock.NewMockMale(ctl) gomock.InOrder( mockMale.EXPECT().Get(id).Return(nil), ) user := NewUser(mockMale) err := user.GetUserInfo(id) if err != nil { t.Errorf("user.GetUserInfo err: %v", err) } }
gomock.Controller
,它表明 mock 生態系統中的頂級控件。定義了 mock 對象的範圍、生命週期和期待值。另外它在多個 goroutine 中是安全的EXPECT()
返回一個容許調用者設置指望和返回值的對象。Get(id)
是設置入參並調用 mock 實例中的方法。Return(nil)
是設置先前調用的方法出參。簡單來講,就是設置入參並調用,最後設置返回值user.GetUserInfo(id)
調用(入參:id 爲 1)中。它調用的是咱們事先模擬好的 mock 方法defer
延遲執行,以防止咱們忘記這一操做回到 mockd/
的根目錄下,執行如下命令
$ go test ./user ok github.com/EDDYCJY/mockd/user
看到這樣的結果,就大功告成啦!你能夠本身調整一下 Return()
的返回值,以此獲得不同的測試結果哦 😄
$ go test -cover ./user ok github.com/EDDYCJY/mockd/user (cached) coverage: 100.0% of statements
可經過設置 -cover
標誌符來開啓覆蓋率的統計,展現內容爲 coverage: 100.0%
。
一、 生成測試覆蓋率的 profile 文件
$ go test ./... -coverprofile=cover.out
二、 利用 profile 文件生成可視化界面
$ go tool cover -html=cover.out
三、 查看可視化界面,分析覆蓋狀況
建議更多的方法可參見 官方文檔
你可能會想一條條命令生成 mock 文件,豈不得崩潰?
固然,官方提供了更方便的方式,咱們能夠利用 go:generate
來完成批量處理的功能
go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]
打開 person/male.go 文件,修改成如下內容:
package person //go:generate mockgen -destination=../mock/male_mock.go -package=mock github.com/EDDYCJY/mockd/person Male type Male interface { Get(id int64) error }
咱們關注到 go:generate
這條語句,可分爲如下部分:
//go:generate
(注意不要留空格)mockgen
命令-destination
-package
source
,此處爲 person 的包路徑interfaces
,此處爲 Male
回到 mockd/
的根目錄下,執行如下命令
$ go generate ./...
再檢查 mock/
發現也已經正確生成了,在多個文件時是否是很方便呢 🤩
在單元測試這一環,gomock 給咱們提供了極大的便利。可以 mock 掉許許多多的依賴項
其中還有不少的使用方式和功能。你能夠 mark 住後詳細閱讀下官方文檔,記憶會更深入