Help us understand the problem. What is going on with this article?

Goでメソッドを簡単にモック化する【gomock】

はじめに

Goでモックを使ったテストを書く際によく利用されるgomockを触ってみました。

gomockとは

https://github.com/golang/mock

テスト用にGoのインターフェースとメソッドをモック化するライブラリです。

インストール

$ go get github.com/golang/mock/gomock
$ go install github.com/golang/mock/mockgen

使ってみる

モックにされる対象のインターフェース

今回は、Barというメソッドを持つFooインターフェースを例にします。

foo.go
type Foo interface {
    Bar(x int) int
}

モックを自動生成する

mockgenコマンドでモックファイルを自動生成します。
-sourceオプションでinterfaceが宣言されているモック対象のファイルを指定し、-destinationオプションでモックファイルの出力先を指定します。
mockgenコマンドには他にも色々オプションがあるみたいです。詳しくはこちら
mockファイルは mock_<package_name>/ ディレクトリ配下に mock_<package_name>.go という名前で作るのが通例ぽいです。

それでは作っていきましょう。

$ mkdir mock_foo
$ mockgen -source foo.go -destination mock_foo/mock_foo.go

生成されたファイルが以下です。
メソッドを実装し、インターフェースを満たす構造体が定義されています。構造体名はディレクトリ名やファイル名に連動しています。

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

// Package mock_foo is a generated GoMock package.
package mock_foo

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

// MockFoo is a mock of Foo interface
type MockFoo struct {
    ctrl     *gomock.Controller
    recorder *MockFooMockRecorder
}

// MockFooMockRecorder is the mock recorder for MockFoo
type MockFooMockRecorder struct {
    mock *MockFoo
}

// NewMockFoo creates a new mock instance
func NewMockFoo(ctrl *gomock.Controller) *MockFoo {
    mock := &MockFoo{ctrl: ctrl}
    mock.recorder = &MockFooMockRecorder{mock}
    return mock
}

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

// Bar mocks base method
func (m *MockFoo) Bar(x int) int {
    m.ctrl.T.Helper()
    ret := m.ctrl.Call(m, "Bar", x)
    ret0, _ := ret[0].(int)
    return ret0
}

// Bar indicates an expected call of Bar
func (mr *MockFooMockRecorder) Bar(x interface{}) *gomock.Call {
    mr.mock.ctrl.T.Helper()
    return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bar", reflect.TypeOf((*MockFoo)(nil).Bar), x)
}

テストファイル

モックを使う簡単な例を示します。
実際にはモックを呼び出すことが目的ではなく、あくまでモックによって外部的要因を固定化して、本当にテストしたい部分をテストする使い方になるはずですが、今回はgomockを理解するためだけなのでご理解ください。

foo_test.go
func TestFoo(t *testing.T) {
    // コントローラーの生成
    ctrl := gomock.NewController(t)

    defer ctrl.Finish()

    // モックの生成
    m := mock_foo.NewMockFoo(ctrl)

    // モックの対象メソッドと引数と返り値を設定する
    m.
        EXPECT().
        Bar(99).
        Return(101)

    // モックの呼び出し
    m.Bar(99)
}

上記テストファイルを実行すると以下の実行結果になります。

成功結果
ok      study/mock  (cached)
Success: Tests passed.

例えば、 m.Bar(100) でモックに設定していない引数で実行すると以下のように失敗します。

失敗結果
--- FAIL: TestFoo (0.00s)
    /Users/XXX/go/src/study/mock/foo_test.go:25: Unexpected call to *mock_foo.MockFoo.Bar([100]) at /Users/XXX/go/src/study/mock/mock_foo/mock_foo.go:38 because: 
        Expected call at /Users/XXX/go/src/study/mock/foo_test.go:22 doesn't match the argument at index 0.
        Got: 100
        Want: is equal to 99
    asm_amd64.s:522: missing call(s) to *mock_foo.MockFoo.Bar(is equal to 99) /Users/XXX/go/src/study/mock/foo_test.go:22
    asm_amd64.s:522: aborting test due to missing call(s)
FAIL
FAIL    study/mock  0.007s
Error: Tests failed.

参考

Go Mockでインタフェースのモックを作ってテストする #golang

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした