sqlxとsqlmockを用いたDB操作モジュールのユニットテスト
概要
-
sqlx
を用いたプログラムのsqlx.DB
部分をモック化してテストしたい
-
sqlmock
を利用することでモック化することに成功
sqlmockを用いたsqlxのモック化サンプルコード
テスト対象モジュール
type ItemRepository struct {
db *sqlx.DB
}
func (r *ItemRepository) FindAll() (model.ItemList, error) {
rows, err := r.db.Queryx(`SELECT * FROM item`)
if err != nil {
return nil, err
}
defer rows.Close()
var itemList model.ItemList
var item model.Item
for rows.Next() {
err := rows.StructScan(&item)
if err != nil {
return nil, err
}
itemList = append(itemList, item)
}
return itemList, nil
}
- 構造体のメンバに
sqlx.DB
を持ち、レシーバでSQLを実行・構造体にデータを詰めて返却するという素朴な実装
モックDB生成関数
func NewMockDB(t *testing.T) (*sqlx.DB, sqlmock.Sqlmock) {
t.Helper()
mockDB, mock, err := sqlmock.New()
if err != nil {
t.Error("MockDB Initialize Failed.")
}
sqlxDB := sqlx.NewDb(mockDB, "sqlmock")
return sqlxDB, mock
}
-
sqlx.NewDb()
の引数にsqlmock
のモックDBを渡すことでsqlx.DB
をモック化している
テストコード
func Test_1(t *testing.T) {
// mock initialize.
db, mock := NewMockDB(t)
defer db.Close()
repos := &ItemRepository{db}
// mock setup.
mock.ExpectQuery(
regexp.QuoteMeta(`SELECT * FROM item`),
).WillReturnRows(
sqlmock.NewRows([]string{""}),
)
// do exec.
_, err := repos.FindAll()
if err != nil {
t.Error(err.Error())
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Error(err.Error())
}
}
- Repositoryのテスト観点は「想定通りのSQLが発行されているか」なのでデータが正常に取得されているかは検証しない
- DBをモック化しているので返却されるデータはモックで設定したものになるに決まっているし、それを検証するのはモック自体のテストになるので意味がない