グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



gomockでGoのインターフェースのmockを作成してテストを実行する

今回はGoのmockフレームワークであるgomockの使い方を紹介したいと思います。

gomockとは、Go言語用のmockフレームワークで、組み込みのテストパッケージと合わせて利用するライブラリです。

インストール方法

$ go get github.com/golang/mock/gomock
go: finding github.com/golang/mock v1.4.4
go: downloading github.com/golang/mock v1.4.4

$ go get github.com/golang/mock/mockgen
go: downloading golang.org/x/tools v0.0.0-20190425150028-36563e24a262
go: extracting golang.org/x/tools v0.0.0-20190425150028-36563e24a262
go: finding golang.org/x/tools v0.0.0-20190425150028-36563e24a262

$ mockgen -version
v1.4.4

テスト対象のサンプルコード

package main

type ApiClient interface {
	Request(string) (string, error)
}

type DataRegister struct {
	client ApiClient // インターフェイスに依存しているだけで実装は存在しない
}

func (d *DataRegister) Register(data string) (string,error) {
	result, err := d.client.Request(data)
	if err != nil {
		return "", err
	}
	return result, nil
}

gomockを使わないで自分でmockを用意した場合のテスト例

package main

import "testing"

// mockを自作する
type ApiClientMock struct {}

func (a *ApiClientMock) Request(data string) (string,error)  {
	return data, nil
}

func TestSample(t *testing.T) {
	d := &DataRegister{}
	d.client = &ApiClientMock{} // mockを登録
	expected := "bar"
	res, err := d.Register(expected)
	if err != nil {
		t.Fatal("Register error!", err)
	}
	if res != expected {
		t.Fatal("Value does not match.")
	}
}

テストの実行

$ go test -v -run TestSample
=== RUN   TestSample
--- PASS: TestSample (0.00s)
PASS
ok      example.com/go-mock-sample     0.001s

gomockを使った場合のテスト例

mockの生成

まずmockファイルを配置するためにmockディレクトリを作成します。

$ mkdir mock

次に対象ファイルを-sourceに指定し、作成したmockの出力先を-destinationに指定してmockgenコマンドを実行します。

$ mockgen -source=sample.go -destination mock/mock_sample.go

すると以下のようなmockファイルが生成されます。

  // Code generated by MockGen. DO NOT EDIT.
// Source: sample.go

// Package mock_main is a generated GoMock package.
package main

import (
        gomock "github.com/golang/mock/gomock"
        reflect "reflect"
)

// MockApiClient is a mock of ApiClient interface
type MockApiClient struct {
        ctrl     *gomock.Controller
        recorder *MockApiClientMockRecorder
}

// MockApiClientMockRecorder is the mock recorder for MockApiClient
type MockApiClientMockRecorder struct {
        mock *MockApiClient
}

// NewMockApiClient creates a new mock instance
func NewMockApiClient(ctrl *gomock.Controller) *MockApiClient {
        mock := &MockApiClient{ctrl: ctrl}
        mock.recorder = &MockApiClientMockRecorder{mock}
        return mock
}

// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockApiClient) EXPECT() *MockApiClientMockRecorder {
        return m.recorder
}

// Request mocks base method
func (m *MockApiClient) Request(arg0 string) (string, error) {
        m.ctrl.T.Helper()
        ret := m.ctrl.Call(m, "Request", arg0)
        ret0, _ := ret[0].(string)
        ret1, _ := ret[1].(error)
        return ret0, ret1
}

// Request indicates an expected call of Request
func (mr *MockApiClientMockRecorder) Request(arg0 interface{}) *gomock.Call {
        mr.mock.ctrl.T.Helper()
        return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Request", reflect.TypeOf((*MockApiClient)(nil).Request), arg0)
}

MockApiClient構造体ががApiClientインターフェイスを実装しているのがわかります。

mockを使ったテストの実装

package main

import (
	"testing"

	"github.com/golang/mock/gomock"
	mock_main "example.com/go-mock-sample/mock"
)

func TestSample(t *testing.T) {
    // mockのコントローラを作成します
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

    // ApiClientインターフェイスのmockを作成します
	mockApiClinet := mock_main.NewMockApiClient(ctrl)
    // 作成したmockに対して期待する呼び出しと返り値を定義します
    // EXPECT()では呼び出されたかどうか
    // Request()ではそのメソッド名が指定した引数で呼び出されたかどうか
    // Return()では返り値を指定します
	mockApiClinet.EXPECT().Request("bar").Return("bar", nil)

	d := &DataRegister{}
	d.client = mockCpiClinet // mockを登録
	expected := "bar"

	res, err := d.Register(expected)
	if err != nil {
		t.Fatal("Register error!", err)
	}
	if res != expected {
		t.Fatal("Value does not match.")
	}
}

テストの実行

$ go test -v -run TestSample
=== RUN   TestSample
--- PASS: TestSample (0.00s)
PASS
ok      example.com/go-mock-sample     0.002s

上記のテストケースでRequestメソッドをコールしなかったり、引数が”bar”でなかった場合にエラーを発生させてくれます。

まとめ

gomockの基本的な使い方をご紹介しました。 今回のような簡単なケースではあまり効果を実感しづらいと思いますが、 大量のテストケースがある場合や複雑なmockを作成する必要がある場合などには便利かと思います。

gomockにはまだまだ便利なメソッドが用意されているので公式のドキュメントを確認してみてください。

また、都度mockファイルを生成するのが面倒な場合はgo generateを使った方法などが紹介されています。

この記事を書いた人

アーティス
アーティス
創造性を最大限に発揮するとともに、インターネットに代表されるITを活用し、みんなの生活が便利で、豊かで、楽しいものになるようなサービスやコンテンツを考え、創り出し提供しています。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします