はじめに
golang/mockをわかりやすく解説したい
要点
・go mockによってテスト中にメソッドの返り値を強制的に設定できる。
・go mockによってテスト中にメソッドが呼ばれない、または指定した引数で呼ばれていないときはテストを失敗にできる。
実際にコードを見てみよう
試しに下のようにsample.goにinterfaceを定義します。
package sample
type Sample interface {
Method(s string) int
}
その後次のコマンドを打ちます。
$ go install github.com/golang/mock/mockgen@v1.6.0
$ mockgen -source sample.go -destination mock/mock_sample.go
そうするとmockディレクトリに下のコードが生成されます。
// Code generated by MockGen. DO NOT EDIT.
// Source: sample.go
// Package mock_sample is a generated GoMock package.
package mock_sample
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockSample is a mock of Sample interface.
type MockSample struct {
ctrl *gomock.Controller
recorder *MockSampleMockRecorder
}
// MockSampleMockRecorder is the mock recorder for MockSample.
type MockSampleMockRecorder struct {
mock *MockSample
}
// NewMockSample creates a new mock instance.
func NewMockSample(ctrl *gomock.Controller) *MockSample {
mock := &MockSample{ctrl: ctrl}
mock.recorder = &MockSampleMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockSample) EXPECT() *MockSampleMockRecorder {
return m.recorder
}
// Method mocks base method.
func (m *MockSample) Method(s string) int {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Method", s)
ret0, _ := ret[0].(int)
return ret0
}
// Method indicates an expected call of Method.
func (mr *MockSampleMockRecorder) Method(s interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Method", reflect.TypeOf((*MockSample)(nil).Method), s)
}
そしてテストコードを下のように記載します。
package sample
import (
"testing"
"github.com/golang/mock/gomock"
"sample/mock" // mod init sample でgo.modを作成
)
func TestSample(t *testing.T) {
// gomock controller作成とclose予約(呪文のようなもの)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// mock instance作成
mockSample := mock.NewMockSample(ctrl)
// 引数"hoge"でMethodが呼ばれることか確認する
mockSample.EXPECT().Method("hoge")
t.Log("result:", mockSample.Method("hoge"))
}
これで go run testをすると普通にテストが通ります。では試しに
t.Log("result:", mockSample.Method("hoge"))
をコメントアウトしてテストしてみましょう。すると下のようにMethodが呼ばれてないとテストが落ちます。
$ go test -v sample_test.go
controller.go:269: missing call(s) to *mock_sample.MockSample.Method(is equal to hoge (string))
--- FAIL: TestSample (0.00s)
では次にコメントアウトを全て外した後に
mockSample.EXPECT().Method("hoge")
の"hoge"を"a"などほかの文字にしてテストを実行してみてください。すると"a"が欲しいのに"hoge"が渡されたと下のエラーが発生します。
Got: hoge (string)
Want: is equal to a (string)
--- FAIL: TestSample (0.00s)
このようにEXPECTメソッドを使用することで、メソッドが想定通りに呼ばれているのか確認することができます。
次に、メソッドの返り値を強制的に決定するReturnメソッドを見てみましょう。
func TestSample(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockSample := mock_sample.NewMockSample(ctrl)
mockSample.EXPECT().Method("hoge").Return(1)
result := mockSample.Method("hoge")
if result != 1 {
t.Errorf("Expected a to be 1, got %d", result)
}
}
ではテストを実行してみましょう。下のように成功しますね。
go test -v sample_test.go
=== RUN TestSample
--- PASS: TestSample (0.00s)
PASS
ok command-line-arguments 0.221s
このようにgomockはメソッドの返り値を設定することが可能となります。他にも呼ばれる順番を確かめるFirstメソッドやSecondメソッド、返り値として関数の実行結果を返せるDoAndReturnメソッドなど便利な機能があります。
参考にさせていただいた記事