regexpパッケージが便利だった
はじめに
クエリのプロセスをモックするgo-sqlmockというライブラリがあったので使ってみたところメタ文字のエスケープが必要になったので便利なものがないか探してみた
実際に使ってみた
repository/account_repository.go
package repository import ( "database/sql" "github.com/sample/sqlmock/domain" ) // AccountRepository repository interface type AccountRepository interface { Update(*domain.Account) error } type accountRepository struct { Conn *sql.DB } // NewAccountRepository new accountrepository func NewAccountRepository(db *sql.DB) AccountRepository { return &accountRepository{ Conn: db, } } // Update update Account func (m *accountRepository) Update(u *domain.Account) error { query := `UPDATE accounts SET password = $1, email = $2, updated_at = now() WHERE id = $3` stmt, err := m.Conn.Prepare(query) if err != nil { return err } _, err = stmt.Exec(u.Password, u.Email, u.ID) if err != nil { return err } return nil }
repository/account_repository_test.go
package repository_test import ( "fmt" "regexp" "testing" "github.com/sample/sqlmock/domain" "github.com/sample/sqlmock/repository" uuid "github.com/satori/go.uuid" "github.com/stretchr/testify/assert" sqlmock "gopkg.in/DATA-DOG/go-sqlmock.v1" ) func TestUpdate(t *testing.T) { u := &domain.Account{ ID: uuid.NewV4(), Email: "example@example.com", Password: "asdf0987", } db, mock, err := sqlmock.New() if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() query := "UPDATE accounts SET password = $1, email = $2, updated_at = now() WHERE id = $3" prep := mock.ExpectPrepare(query) prep.ExpectExec().WithArgs(u.Password, u.Email, u.ID).WillReturnResult(sqlmock.NewResult(1, 1)) a := repository.NewAccountRepository(db) err = a.Update(u) assert.NoError(t, err) return }
詰まったところ
テストのmock.ExpectPrepare()
のときにqueryの文字列をそのまま渡すとエラーになってテストが失敗していた
$ go test -v === RUN TestUpdate --- FAIL: TestUpdate (0.00s) Error Trace: account_repository_test.go:32 Error: Received unexpected error: Prepare query string 'UPDATE accounts SET password = $1, email = $2, updated_at = now() WHERE id = $3', does not match regex [UPDATE accounts SET password = $1, email = $2, updated_at = now() WHERE id = $3] Test: TestUpdate FAIL exit status 1 FAIL github.com/sample/sqlmock/repository 0.020s
go-sqlmockのissueに上がっていたので見てみた どうやらメタ文字をエスケープしないとエラーが返ってくるみたいだ
つまりaccount_repository_test.goのqueryをこうすればテストが成功する
query := "UPDATE accounts SET password = \$1, email = \$2, updated_at = now\(\) WHERE id = \$3"
しかし、毎回エスケープするのも面倒なのでできれば関数か何かを通してエスケープできるようにしたい
regexpのQuoteMeta
QuoteMetaは、引数text内のすべての正規表現メタ文字を引用符で囲む文字列を返す。
リテラルテキストに一致する正規表現が返ってくる。
例えば、QuoteMeta( \[foo\]
)は \ \[foo \\\]
を返す。
これをaccount_repository_test.goに書くとこうなる
query := "UPDATE accounts SET password = $1, email = $2, updated_at = now() WHERE id = $3" prep := mock.ExpectPrepare(regexp.QuoteMeta(query))
これでテストが通るようになった
所感
regexpパッケージは便利
だけど処理速度が遅いのでアプリ自体にQuoteMetaとかは使わないほうがいいのかなと
テストに使う分には十分便利