0
1

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 1 year has passed since last update.

sqlmock + gormでDB周りのテストを書く

Last updated at Posted at 2022-04-25

sqlmockとは

Goのテスト用のDBを用意しなくてもSQLドライバのような振る舞いをしてくれるモックのライブラリ

パッケージのバージョン

go.mod
gorm.io/driver/postgres v1.3.4 // indirect
gorm.io/gorm v1.23.1 // indirect

困ったこと

gorm.io/driver/mysqlのv1.3.2では正しく動作しない。v1.0.3では正しく動作する。
Where("id=?", 1) ではなく Where("id = ?", 1) と記述する。

解決フロー

このような困った仕様に気づくために記事を見るだけでなく実際に動くコードをGitHubからcloneしてきてコードを色々変えて試してみることがとても大切だと感じた。Versionも確認すること。

実際のコード

package database_test

import (
	"fmt"
	"regexp"
	"testing"
	"time"
	"github.com/DATA-DOG/go-sqlmock"
	"github.com/ymktmk/golang-clean-architecture/domain"
	"github.com/ymktmk/golang-clean-architecture/infrastructure"
	"github.com/ymktmk/golang-clean-architecture/interfaces/database"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

func NewDbMock() (*gorm.DB, sqlmock.Sqlmock, error) {
	sqlDB, mock, err := sqlmock.New()
	mockDB, err := gorm.Open(postgres.New(postgres.Config{
		Conn: sqlDB,
	}), &gorm.Config{})
	return mockDB, mock, err
}

func DummyHandler(conn *gorm.DB) database.SqlHandler {
	sqlHandler := new(infrastructure.SqlHandler)
	sqlHandler.Conn = conn
	return sqlHandler
}

func TestStore(t *testing.T) {
	mockDB, mock, err := NewDbMock()
	if err != nil {
		t.Errorf("Failed to initialize mock DB: %v", err)
	}
	
	u := &domain.User{
		Name: "sheep",
		Email: "example@gmail.com",
	}
	
	// mock設定
	rows := sqlmock.NewRows([]string{"id"}).AddRow(1)

	mock.ExpectBegin()
	mock.ExpectQuery(regexp.QuoteMeta(
		`INSERT INTO "users" ("created_at","updated_at","deleted_at","name","email") VALUES ($1,$2,$3,$4,$5)`)).
		WillReturnRows(rows)
	mock.ExpectCommit()

	// repository 初期化
	repo := &database.UserRepository{SqlHandler: DummyHandler(mockDB)}
	user, err := repo.Store(u)
	fmt.Println(user)
	if err != nil {
		t.Fatal(err)
	}
	
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("Test Create User: %v", err)
	}
}


func TestFindById(t *testing.T) {
	mockDB, mock, err := NewDbMock()
	if err != nil {
		t.Errorf("Failed to initialize mock DB: %v", err)
	}

	// mock設定
	rows := sqlmock.NewRows([]string{"id", "name", "email", "created_at", "updated_at", "deleted_at"}).
	AddRow(1, "tomoki", "example@gmail.com", time.Now(), time.Now(), nil)
	
	mock.ExpectQuery(regexp.QuoteMeta(
		`SELECT * FROM "users" WHERE id = $1`)).
		WithArgs(1).
		WillReturnRows(rows)

      // repository 初期化
	repo := &database.UserRepository{SqlHandler: DummyHandler(mockDB)}
	user, err := repo.FindById(1)
	fmt.Println(user)
	if err != nil {
		t.Fatal(err)
	}

	if err := mock.ExpectationsWereMet(); err != nil {
		t.Errorf("Test Find User: %v", err)
	}
}

マイリポジトリ

動くものをサンプルとして公開しています。よかったら試してみてください。

参考文献

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?