Go言語でmockを使う方法

Go言語

Go言語でテストコードを書く時などに、mockを使いたくなった時があると思います。
この記事を読むことで、どのようにmockを作成するかがわかります。
Go言語でmockを作成する場合、gomockというmockのフレームワークを使用します。
インストールからmock作成までを説明します。

インストール

gomockとmockgenをインストール

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

実際に試したこと

repositoryとusecaseを作成して、usecaseからrepositoryを呼んで処理をするコードを書く。
テストコード(user_test.go)ではrepositoryの処理はrepository_mockを使用するようにする。

ディレクトリ構成

.
├── repository
│   ├── repository_mock
│   │   └── user_mock.go
│   └── user.go
└── usecase
    ├── user.go
    └── user_test.go

repository/user.go

UserのRepositoryのインターフェースを定義している
「//go:generate mockgen~」はgo generateのコマンドで、mockを作成できるようにソースに追記をしている。

package repository

type User struct {
	ID   int
	Name string
	Age  int
}

//go:generate mockgen -source=./user.go -destination=./repository_mock/user_mock.go -package=repository_mock
type UserRepository interface {
	Get(int) (*User, error)
	Create(*User) (*User, error)
}

repository/repository_mock/user_mock.go(作成されたmockのソース)

ルートディレクトリで以下のコマンドを実行してuser_mock.goを作成

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

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

import (
	reflect "reflect"
	repository "test/gomock/repository"

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

// MockUserRepository is a mock of UserRepository interface.
type MockUserRepository struct {
	ctrl     *gomock.Controller
	recorder *MockUserRepositoryMockRecorder
}

// MockUserRepositoryMockRecorder is the mock recorder for MockUserRepository.
type MockUserRepositoryMockRecorder struct {
	mock *MockUserRepository
}

// NewMockUserRepository creates a new mock instance.
func NewMockUserRepository(ctrl *gomock.Controller) *MockUserRepository {
	mock := &MockUserRepository{ctrl: ctrl}
	mock.recorder = &MockUserRepositoryMockRecorder{mock}
	return mock
}

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

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

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

// Get mocks base method.
func (m *MockUserRepository) Get(arg0 int) (*repository.User, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "Get", arg0)
	ret0, _ := ret[0].(*repository.User)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

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

usecase/user.go

repositoryを使ってUserの作成や取得を行う処理
テストコードではmockを使用する

package usecase

import "test/gomock/repository"

type UserUsecase struct {
	repository repository.UserRepository
}

func (u *UserUsecase) List(id int) (*repository.User, error) {
	return u.repository.Get(id)
}

func (u *UserUsecase) Create(name string, age int) (*repository.User, error) {
	user := &repository.User{
		Name: name,
		Age:  age,
	}
	return u.repository.Create(user)
}

usecase/user_test.go(テストコード)

mockを使ってテストをすることで、repository/user.goに依存せずに簡単にusecaseのCreateのテストができる。

package usecase

import (
	"test/gomock/repository"
	"test/gomock/repository/repository_mock"
	"testing"

	"github.com/golang/mock/gomock"
	"github.com/google/go-cmp/cmp"
)

func TestCreate(t *testing.T) {
	user := &repository.User{
		Name: "taro",
		Age:  40,
	}

	// mockの作成
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()
	mock := repository_mock.NewMockUserRepository(mockCtrl)
	mock.EXPECT().Create(user).Return(
		&repository.User{
			ID:   1,
			Name: "taro",
			Age:  40,
		}, nil,
	)
	var expect *repository.User = &repository.User{
		ID:   1,
		Name: "taro",
		Age:  40,
	}

	usecase := &UserUsecase{
		repository: mock,
	}

	// 結果チェック
	result, err := usecase.repository.Create(user)
	if err != nil {
		t.Error("error happen")
	}
	if diff := cmp.Diff(result, expect); diff != "" {
		t.Errorf("User Data miss match :%s", diff)
	}
}

まとめ

今回はGo言語で、mockを生成するgomockについて紹介をしてきました。
Go言語について他にselectの仕組みなどの記事を書いておりますので、興味がありましたらそちらも読んでもらたらと思います。

【おすすめ記事のリンク】

コメント

タイトルとURLをコピーしました

Fatal error: Uncaught JSMin_UnterminatedStringException: JSMin: Unterminated String at byte 699: "ほとんどの言語で、テストコードを書くにあたってmockを使用するこが多いと思います。 in /home/c5287456/public_html/engineer-want-to-grow.com/wp-content/plugins/autoptimize/classes/external/php/jsmin.php:214 Stack trace: #0 /home/c5287456/public_html/engineer-want-to-grow.com/wp-content/plugins/autoptimize/classes/external/php/jsmin.php(152): JSMin->action(1) #1 /home/c5287456/public_html/engineer-want-to-grow.com/wp-content/plugins/autoptimize/classes/external/php/jsmin.php(86): JSMin->min() #2 /home/c5287456/public_html/engineer-want-to-grow.com/wp-content/plugins/autoptimize/classes/external/php/ao-minify-html.php(257): JSMin::minify('{"@context":"ht...') #3 [internal function]: AO_Minify_HTML->_removeScriptCB(Array) #4 /home/c5287456/public_html/engineer-want-to-grow.com/wp-content/plugins/autoptimize/classes/external/php/ao-minify-html.php(108): preg_replace_callback('/(\\s*)(<script\\... in /home/c5287456/public_html/engineer-want-to-grow.com/wp-content/plugins/autoptimize/classes/external/php/jsmin.php on line 214