👌
Go言語でテスト対象が呼び出すファンクションをmock化して、パラメーターをインメモリDBに登録して検証する
初めに
Go言語のテストコードで、テスト対象のファンクション内で呼び出すファンクションのパラメーターを検証する際に、そのパラメーターをインメモリDBに登録し、その内容を検証して、実際に期待通りの値で呼び出されたかを確認する方法を説明します。
仕様ライブラリ
テスト対象
内部でNewExample()
によって注入されたUserRepository
のStore(name, address string, age int) error
を呼び出し、name
、address
、age
のユーザー情報を登録する処理です。
この処理で呼び出されるStore()
がMockの対象です。
package example
type UserRepository interface {
// Mock対象
Store(name, address string, age int) error
}
type Example interface {
RegisterUser(name, address string, age int) error
}
type example struct {
userRepository UserRepository
}
func NewExample(userRepository UserRepository) Example {
return &example{userRepository: userRepository}
}
// テスト対象
func (e *example) RegisterUser(name, address string, age int) error {
return e.userRepository.Store(name, address, age)
}
テストコード
package example
import (
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"gorm.io/driver/mysql"
"gorm.io/gorm"
sqle "github.com/dolthub/go-mysql-server"
"github.com/dolthub/go-mysql-server/memory"
"github.com/dolthub/go-mysql-server/server"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/types"
)
var (
dbName = "mydb"
tableName = "users"
address = "localhost"
port = 3306
mysqlServer *server.Server
testMemoryDB *gorm.DB
)
// gorm usersテーブルモデル
type User struct {
ID int64 `gorm:"primaryKey;autoIncrement:false"`
Name string
Address string
Age int32
}
type userRepositoryMock struct{}
func NewUserRepositoryMock() UserRepository {
return &userRepositoryMock{}
}
func (r userRepositoryMock) Store(name, addr string, age int) error {
// インメモリDBに引数を登録
return testMemoryDB.Debug().Create(User{Name: name, Address: addr, Age: int32(age)}).Error
}
func TestMain(m *testing.M) {
// インメモリDB起動
startTestMemoryDB()
// gormでインメモリDBに接続
initGorm()
m.Run()
if err := mysqlServer.Close(); err != nil {
panic(err)
}
}
func Test(t *testing.T) {
example := NewExample(NewUserRepositoryMock())
// テスト対象呼び出し
err := example.RegisterUser("テスト 太郎", "東京都千代田区", 20)
if err != nil {
t.Error(err)
}
// インメモリDBに登録された引数の内容を取得
results := findUsers()
// インメモリDBに登録された内容を検証
expected := []User{{ID: 1, Name: "テスト 太郎", Address: "東京都千代田区", Age: 20}}
if diff := cmp.Diff(expected, results, nil); diff != "" {
t.Errorf("registered users is mismatch (-expected +actual):%s¥n", diff)
}
}
func startTestMemoryDB() {
db := memory.NewDatabase(dbName)
db.BaseDatabase.EnablePrimaryKeyIndexes()
pro := memory.NewDBProvider(db)
// Store()呼び出し時のパラメーターを登録するテーブル作成
table := memory.NewTable(db, tableName, sql.NewPrimaryKeySchema(sql.Schema{
{Name: "id", Type: types.Int64, Nullable: false, Source: tableName, AutoIncrement: true, PrimaryKey: true},
{Name: "name", Type: types.Text, Nullable: false, Source: tableName},
{Name: "address", Type: types.Text, Nullable: false, Source: tableName},
{Name: "age", Type: types.Int32, Nullable: false, Source: tableName},
}), db.GetForeignKeyCollection())
db.AddTable(tableName, table)
engine := sqle.NewDefault(pro)
mysqlConfig := server.Config{
Protocol: "tcp",
Address: fmt.Sprintf("%s:%d", address, port),
}
s, err := server.NewServer(mysqlConfig, engine, memory.NewSessionBuilder(pro), nil)
if err != nil {
panic(err)
}
mysqlServer = s
go s.Start()
}
func initGorm() {
dsn := "root:@tcp(localhost:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local"
gormDB, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
testMemoryDB = gormDB
}
func findUsers() []User {
var results []User
err := testMemoryDB.Debug().Find(&results).Error
if err != nil {
panic(err)
}
return results
}
テストの流れ
おおまかに、下記のような流れとなります。
-
startTestMemoryDB()
でgo-mysql-server
を使用して、インメモリDB起動
1-1. DB作成
1-2.id
(PK, オートインクリメント),name
,address
,age
の4つのカラムをもつテーブル(users
)を作成
1-3. DBサーバー起動 -
initGorm()
で上記で作成したインメモリDBにGORMで接続 - テスト実行
3-1.RegisterUser()
が内部で呼び出すStore()
を、インメモリDBのusersテーブルに登録するMock実装の内容で注入
3-2. テスト対象のRegisterUser()
呼び出し
3-3.Store()
は1でMockされたものが呼び出され、RegisterUser(name, address string, age)
の3つの引数がそのままStore()
呼び出し時のパラメーターに指定され、対応する値がusers
テーブルの各カラムに登録されます。(id
はオートインクリメントの値として定義しているので、登録時にUser
モデルに指定しなければ自動で登録されます)
3-4. インメモリDBに登録された引数の内容を取得(GORMでSELECT * FROM users
のクエリを発行)
3-5. 3-3で取得した内容をgo-cmp
で検証
最後に
Go言語のMockの検証だと、github.com/stretchr/testify/mock
を使用していましたが、今回はインメモリDBを作成して、そこに呼び出し時のパラメータを登録し、その内容を検証する方法でテストコードを実装してみました。
テスト対象に応じて上手く使い分けて、保守性に優れ、価値あるテストコードが作れるようになればと思います。
Discussion