go generateでモックを生成して呼び出すまで
今更感あるがgo generateを使ってモックを生成して実際にテストコードなどで呼び出すところまで触ってみたので記録として残しておく。
構成
modelとrepositoryを作る。
このrepositoryで定義したInterfaceを利用可能なモックをmockディレクトリ配下に生成してmain_test.goでそのmockを利用したテストコードを実装する。
├── domain │ ├── model │ │ └── item_model.go │ └── repository │ └── item_repository.go ├── go.mod ├── go.sum ├── main_test.go └── mock
一応ソース置いておきます。
model
とりあえず簡単なモデルを作ります。
domain/model/item/item_model.go
package model type ItemModel struct { Id int Name string }
repository
Itemモデルのリポジトリを簡易的に作る。
domain/repository/item/item_repository.go
//go:generate mockgen -source=$GOFILE -package=mock_$GOPACKAGE -destination=../../../mock/$GOPACKAGE/$GOFILE package repository import "github.com/ybalexdp/go-generate-sample/domain/item/model" type ItemGetter interface { Get(id int) (item model.ItemModel, err error) }
この際に以下の一文を冒頭に追記する。(オプションに指定する内容は適宜読み替えてください)
//go:generate mockgen -source=$GOFILE -package=mock_$GOPACKAGE -destination=../../mock/$GOPACKAGE/$GOFILE
go generate
オプションなど
今回指定したオプションについては以下の通り。
オプション | 内容 |
---|---|
-source | 対象のファイル名 |
-package | 生成されたmockファイルに設定するpackage名 |
-destination | mockファイルの生成先 |
また設定できる変数に関しては以下のようです。詳細はこちら
Go generate sets several variables when it runs the generator: $GOARCH The execution architecture (arm, amd64, etc.) $GOOS The execution operating system (linux, windows, etc.) $GOFILE The base name of the file. $GOLINE The line number of the directive in the source file. $GOPACKAGE The name of the package of the file containing the directive. $GOROOT The GOROOT directory for the 'go' command that invoked the generator, containing the Go toolchain and standard library. $DOLLAR A dollar sign.
実行
$ go generate ./domain/repository/item_repository.go
これで mock/repository/item_repository.go
が生成されている。
内容は以下の通り。
// Code generated by MockGen. DO NOT EDIT. // Source: item_repository.go // Package mock_repository is a generated GoMock package. package mock_repository import ( reflect "reflect" gomock "github.com/golang/mock/gomock" model "github.com/ybalexdp/go-generate-sample/domain/model" ) // MockItemGetter is a mock of ItemGetter interface. type MockItemGetter struct { ctrl *gomock.Controller recorder *MockItemGetterMockRecorder } // MockItemGetterMockRecorder is the mock recorder for MockItemGetter. type MockItemGetterMockRecorder struct { mock *MockItemGetter } // NewMockItemGetter creates a new mock instance. func NewMockItemGetter(ctrl *gomock.Controller) *MockItemGetter { mock := &MockItemGetter{ctrl: ctrl} mock.recorder = &MockItemGetterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockItemGetter) EXPECT() *MockItemGetterMockRecorder { return m.recorder } // Get mocks base method. func (m *MockItemGetter) Get(id int) (model.ItemModel, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get", id) ret0, _ := ret[0].(model.ItemModel) ret1, _ := ret[1].(error) return ret0, ret1 } // Get indicates an expected call of Get. func (mr *MockItemGetterMockRecorder) Get(id interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockItemGetter)(nil).Get), id) }
テストコード
実際にテストコードを書いてmockを呼び出してみる。
package main import ( "testing" "github.com/golang/mock/gomock" "github.com/ybalexdp/go-generate-sample/domain/model" mock_repository "github.com/ybalexdp/go-generate-sample/mock/repository" ) func TestSample(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockApiClient := mock_repository.NewMockItemGetter(ctrl) expected := model.ItemModel{Id: 1, Name: "hoge"} mockApiClient.EXPECT().Get(1).Return(model.ItemModel{Id: 1, Name: "hoge"}, nil) res, err := mockApiClient.Get(1) if err != nil { t.Errorf("error! %v", err) } if res != expected { t.Errorf("Get() = %v want %v", res, expected) } }
以下で実際に呼び出すメソッドと与える引数、そしてReturnで返却される値を定義できる。
mockApiClient.EXPECT().Get(1).Return(model.ItemModel{Id: 1, Name: "hoge"}, nil)
テスト実行してみる
$ go test -v === RUN TestSample --- PASS: TestSample (0.00s) PASS ok github.com/ybalexdp/go-generate-sample 0.336s