2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

GoでMockを用いてテストする方法

Last updated at Posted at 2019-11-08

はじめに

最近GoでMockを用いてテストを行うことが多いのですが,その手法がシンプルで使いやすいので紹介したいと思います.

実装

今回はDBからユーザーを全件取得して,指定したユーザーの年齢以上のユーザーのみを抜き取るという処理に対するテストを考えます.(そんなことはDB側で処理すればいいですが,いい例が思いつかなかったので悪しからず)
まずはUserのModel定義をします

user.go
type User struct {
	ID   int
	Name string
	Age  int
}

次にDBに関する実装を行いますが,interfaceとその具体実装の2つに分けて書きます.

user_db.go
type UserDB interface {
	GetAllUsers() ([]User, error)
}

type UserDBImpl struct {
}

func (db *UserDBImpl) GetAllUsers() ([]User, error) {
	// 全Userを返す処理
}

ここでUserDBはinterfaceで,UserDBImplはその具体的な実装をする構造体となっています.今回は全てのユーザーを取得する関数であるGetAllUsersをUserDBImplのレシーバとして定義しています.本番のサービスで用いる実際のコード(ORMを用いて実際のデータを取得してくるなど)はこのGetAllUsersに定義します.
次に,このUserDBを用いて年齢によってユーザーを絞り込む処理を実装します.

user_handler.go
type UserHandler struct {
	DB UserDB
}

func (handler *UserHandler) FilterUsers(age int) ([]User, error) {
	allUsers, err := handler.DB.GetAllUsers()
	if err != nil {
		return []User{}, err
	}

	filteredUsers := []User{}
	for _, user := range allUsers {
		if user.Age >= age {
			filteredUsers = append(filteredUsers, user)
		}
	}

	return filteredUsers, nil
}

そんなに難しい処理は書いていないので詳細は省きますが, 重要な部分はUserHandlerがinterfaceであるUserDB をフィールドに持っている点です.これによってUserHandlerのDBにはUserDBで実装しているメソッドと同じメソッドを実装している構造体をなんでも入れることができるようになります(UserDBImplもその1つ).

最後に,このFilterUsersに関するテストを書いていくことにします.

user_handler_test.go
import (
	"reflect"
	"testing"
)

type UserDBImplMock struct {
}

func (db *UserDBImplMock) GetAllUsers() ([]User, error) {
	users := []User{
		User{
			ID:   1,
			Name: "user1",
			Age:  10,
		},
		User{
			ID:   2,
			Name: "user2",
			Age:  25,
		},
		User{
			ID:   3,
			Name: "user3",
			Age:  16,
		},
		User{
			ID:   4,
			Name: "user4",
			Age:  33,
		},
		User{
			ID:   5,
			Name: "user5",
			Age:  18,
		},
	}

	return users, nil
}

func TestFilterUsers(t *testing.T) {
	mock := UserDBImplMock{}
	handler := UserHandler{&mock}
	input := 20
	expect := []User{
		User{
			ID:   2,
			Name: "user2",
			Age:  25,
		},
		User{
			ID:   4,
			Name: "user4",
			Age:  33,
		},
	}
	output, err := handler.FilterUsers(input)

	if err != nil {
		t.Error(err.Error())
	}

	if !reflect.DeepEqual(&output, &expect) {
		t.Errorf("output: %+v \n expect: %+v", output, expect)
	}
}

UserDBImplMockはUserDBのメソッドを実装したもので,こちらで実装するメソッドでは名前の通りMockデータを返すようにします.実際,GetAllUsersではDBから取得されると期待されるデータを定義し,それを返すようにしています.
実際にテストしたいメソッドであるTestFilterUsersでは,はじめにUserDBImplMockを用いてUserHandlerを初期化しています.これがUserHandlerがinterfaceをフィールドに持っていることの良い点で,これにより実際のDBを考えずともMockデータでテストができるようになるわけです.

さいごに

Goではinterfaceに関してプログラミングを行うことで非常に簡単にMockによるtestを行うことができます.サービスのアーキテクチャによっては重宝するかと思うので,ぜひ試してみてください.

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?