2
0

【Golang】GORMでPostgres利用時のCreateをgo-sqlmockで単体テスト

Last updated at Posted at 2024-02-16

はじめに

 近年WEBアプリケーションのバックエンドAPIにGolangを採用することが増えている。それに伴いGORMをORマッパーに採用することも少なくないだろう。
 本記事ではデータベースにPostgresを採用時、GORMで実装したInfrastructure層のCreate関数を単体テストする。

Domain層

 Infrastructure層の単体テストを行うにあたり、本記事で利用するリソースの抜粋を以下に示す。

domain/user.go
type User struct {
    gorm.Model
    Name  string `gorm:"not null"`
    Email string `gorm:"uniqueIndex;not null"`
}

type UserRepository interface {
    Create(*gorm.DB, *User) error
}

Infrastructure層

今回単体テストを行うCreate関数を以下に示す。

infrastructure/persistence/user.go
type userPersistence struct {}

func (up userPersistence) Create(db *gorm.DB, user *domain.User) error {
    return db.Create(user).Error
}

単体テスト

 本記事の目的である単体テストを行うプログラムを以下に示す。

infrastructure/persistence/user_test.go
type AnyTime struct{}

func (a AnyTime) Match(v driver.Value) bool {
	_, ok := v.(time.Time)
	return ok
}

func TestCreateUser(t *testing.T) {
    user := &domain.User{
		Name:  "Testing"
        Email: "test@sample.com"
	}

	sqlDB, mock, err := sqlmock.New()
	if err != nil {
		t.Errorf(err.Error())
	}

	db, err := gorm.Open(postgres.New(postgres.Config{Conn: sqlDB}), &gorm.Config{})
	if err != nil {
		t.Errorf(err.Error())
	}

	mock.ExpectBegin()
	mock.ExpectQuery(regexp.QuoteMeta(`INSERT INTO "users"`)).
		WithArgs(AnyTime{}, AnyTime{}, nil, user.Name, user.Email).
		WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))
	mock.ExpectCommit()

	p := NewUserPersistence()
	if err := p.Create(db, user); err != nil {
		t.Errorf(err.Error())
	}

	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf(err.Error())
	}
}

 ここで注目したいのがmock.ExpectQueryである。go-sqlmockのGitHub内Readmeに、INSERT INTOのテストはmock.ExpectExecで行うと記載されている。本来、INSERT, UPDATE, DELETEなどのsqlはExec、selectなどのsqlはQueryで実行される。簡単に説明すると、Execでの実行は戻り値がなく、Queryでの実行は戻り値がある、と考えて問題ない。
 ではなぜINSERTなのにQueryで実装されるのか。これはDBがPostgresの際にGORMが作成するsqlにRETURNINGが含まれるからである。GORMが作成するsqlを以下に示す。

INSERT INTO "users" ("created_at","updated_at","deleted_at","name","email") VALUES ($1,$2,$3,$4,$5) RETURNING "id"

RETURNINGはPostgresのみの機能(MySQLにはない)で、実行時に戻り値を設定する。GORMのCreate関数が作成するsqlはIDを返すようになっているため、Execでの実行ではなくQueryでの実行になり、mock.ExpectQueryでテストを行う必要がある。これはgo-sqlmockのissueで言及されている。

まとめ

 本記事ではPostgresのDBを利用時GORMで実装したCreate関数を、go-sqlmockで単体テストを行う手法をまとめた。詳しいtestingやgo-sqlmockの記載方法、DataBaseのExecとQueryの違いなどは公式サイトを確認してほしい。
 では、良いエンジニアライフを。

2
0
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
0