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

一応ソース置いておきます。

github.com

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

参考