gormを使ったコードのテストをするとき、Dockerなどを使ってDBを立ち上げてテストする必要がありますが、go-sqlmock
を使うと、実際のDBの代わりにモックを使ってテストすることができます。
この記事は、簡単なモデルを例にしたサンプルコードです。
パッケージのインストール
gormとgo-sqlmockは以下のコマンドでインストールできます。
go get -u github.com/jinzhu/gorm
go get -u github.com/DATA-DOG/go-sqlmock
テスト対象のソース
RepositoryパターンでCreateとGetをGormで実装した例です。
user/user.go
package user
import (
"github.com/jinzhu/gorm"
)
type User struct {
ID string `gorm:"primary_key"`
Name string
}
type Repository struct {
*gorm.DB
}
func (p *Repository) Create(id string, name string) error {
person := &User{
ID: id,
Name: name,
}
return p.DB.Create(person).Error
}
func (p *Repository) Get(id string) (*User, error) {
var person User
err := p.DB.Where("id = ?", id).Find(&person).Error
return &person, err
}
テストコード
先程のコードの、CreateとGetをそれぞれテストしてみます。
まず、DBモックとGORMのオープンです。
user/user_test.go
package user
import (
"regexp"
"testing"
sqlmock "github.com/DATA-DOG/go-sqlmock"
"github.com/jinzhu/gorm"
)
func getDBMock() (*gorm.DB, sqlmock.Sqlmock, error) {
db, mock, err := sqlmock.New()
if err != nil {
return nil, nil, err
}
gdb, err := gorm.Open("postgres", db)
if err != nil {
return nil, nil, err
}
return gdb, mock, nil
}
これを使ってテストを書いてみます。
Createのテスト
user/user_test.go
func TestCreate(t *testing.T) {
db, mock, err := getDBMock()
if err != nil {
t.Fatal(err)
}
defer db.Close()
db.LogMode(true)
r := Repository{DB: db}
id := "2222"
name := "BBBB"
// Mock設定
mock.ExpectQuery(regexp.QuoteMeta(
`INSERT INTO "users" ("id","name") VALUES ($1,$2)
RETURNING "users"."id"`)).
WithArgs(id, name).
WillReturnRows(
sqlmock.NewRows([]string{"id"}).AddRow(id))
// 実行
err = r.Create(id, name)
if err != nil {
t.Fatal(err)
}
}
mock.ExpectQuery
のところで、期待するSQLとパラメータと、実行結果のレコードを設定しています。
実際に実行した時に、この期待結果と異なる場合、以下のようなエラーが返ります。
--- FAIL: TestCreate (0.00s)
user_test.go:48: Query 'INSERT INTO "users" ("id","name") VALUES ($1,$2) RETURNING "users"."id"', arguments do not match: argument 0 expected [string - 2222X] does not match actual [string - 2222]
Getのテスト
user/user_test.go
func TestGet(t *testing.T) {
db, mock, err := getDBMock()
if err != nil {
t.Fatal(err)
}
defer db.Close()
db.LogMode(true)
r := Repository{DB: db}
id := "1111"
name := "AAAA"
// Mock設定
mock.ExpectQuery(regexp.QuoteMeta(
`SELECT * FROM "users" WHERE (id = $1)`)).
WithArgs(id).
WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).
AddRow(id, name))
// 実行
res, err := r.Get(id)
if err != nil {
t.Fatal(err)
}
if res.ID != id || res.Name != name {
t.Errorf("取得結果不一致 %+v", res)
}
}