Goのgomockの使い方を初心者向けに解説する

thumnail
2020-04-30

gomockを利用した背景を軽く話す。cognitoのユーザープールからユーザー情報を引っ張ってきて、その情報を元にある情報を取得するというAPIを開発していた。このAPIを生成するにあたって、AWS cognitoからユーザー情報を取得するという部分の処理をスタブ化する必要が発生した。というのも原則として外部状態に依存するテストは良くないからだ。理由は次の通り。

・ネットワークの問題が発生したときテストに失敗する
・外部サービスが従量課金制度の場合、テストごとに料金が発生する

これによりGoでスタブを生成するパッケージを探していた所このgomockと呼ばれるパッケージがよく使われているようなので使うことにした。

gomockとは

https://github.com/golang/mock

例えばhoge.goというファイルがあったときにgo mockを使えばmock_go.goといった感じにhoge.goのモックを一瞬で生成してくれる。

gomockをインストールする

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

gomockでモックを生成するhoge.goという以下のファイルがあるとして

package hoge

type Hoge interface {
        Foo(s string) int
}

このコードをモック化する。モック化するにあたって、mockgenというコマンドを叩く。

$ mockgen -source hoge.go -destination mock_hoge.go

destinationコマンドで生成されるモックファイルの名前とディレクトリを指定することができる。そして生成されたmock_hoge.goはこんな感じ。

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

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

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

// MockHoge is a mock of Hoge interface
type MockHoge struct {
    ctrl     *gomock.Controller
    recorder *MockHogeMockRecorder
}

// MockHogeMockRecorder is the mock recorder for MockHoge
type MockHogeMockRecorder struct {
    mock *MockHoge
}

// NewMockHoge creates a new mock instance
func NewMockHoge(ctrl *gomock.Controller) *MockHoge {
    mock := &MockHoge{ctrl: ctrl}
    mock.recorder = &MockHogeMockRecorder{mock}
    return mock
}

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

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

// Foo indicates an expected call of Foo
func (mr *MockHogeMockRecorder) Foo(s interface{}) *gomock.Call {
    mr.mock.ctrl.T.Helper()
    return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Foo", reflect.TypeOf((*MockHoge)(nil).Foo), s)
}

ファイル更新時に自動でモックを生成する

これは少し踏み入った話だが、モック対象ファイル、今回で言うhoge.goが更新された時、いちいちmockgenコマンドを叩くのは正直めんどくさい。これを回避する方法がある。インターフェースが定義されているモック対象ファイルに限って、先頭の行に以下のコメントを付与すれば良い。

//go:generate mockgen -source=$GOFILE -destination=mock_$GOFILE -package=$GOPACKAGE

こうすることでインターフェースを修正して、go generateが実行すれば最新のモックが生成される。

gomockでスタブを生成するmock_hoge.goを使ってスタブを生成してみる。(実装の都合上mock_hoge.goのpackageをhogeに変更する)

スタブを使ったテストコードを書くためにhoge_test.goというファイルを用意した。

package hoge

import (
        "testing"

        "github.com/golang/mock/gomock"
)

func TestHoge1(t *testing.T) {
        ctrl := gomock.NewController(t)
        defer ctrl.Finish()

        m := NewMockHoge(ctrl)
        m.EXPECT().Foo("hoge").Return(1)

        t.Log("result:", m.Foo("hoge"))
}

mock instanceを生成して、EXPECTでスタブを生成することができ、Returnでその関数の返り値を定義することができる。このテストコードを実行してみる。出力に1が出てきたらスタブができていることが証明できる。

$ go test -v -timeout 30s tmp/hoge 
=== RUN   TestHoge1
        TestHoge1: hoge_test.go:16: result: 1
--- PASS: TestHoge1 (0.00s)
PASS
ok      tmp/hoge        0.004s

result: 1が出力されているので無事にスタブを生成することができた。大枠はだいたいこんな感じ。細かい設定などはREADMEを参考に。

https://github.com/golang/mock

profile

KATUO

web developer

六本木で月間利用者数数千万のサービスを運営するミドルベンチャーでWeb系エンジニアをやってます。フロントエンドはVue.js/Nuxt.js,サーバーサイドはGolang, クラウドはAWS, GCPを良く扱います。お仕事・イベント等の依頼はContactのフォームからお願いします。

KATUBLO

Copyright since 2018 KATUO All Rights Reserved.