【OSSから学ぶ】Golang製ライブラリDATA-DOG/go-sqlmockを読んだ
はじめに
OSSのコードには自分が今まで学んできた原理原則やデザインパターンが多く散りばめられていました。
今回はこちらのOSSのコードを読んで学んだ面白い実装を備忘録として残しておきます。
学んだコードの概要
DATA-DOG/go-sqlmockは、SQLのテストをサポートしてくれるGo製のライブラリです。
その中でも今回は、SQLの実測値と期待値を比較する箇所の実装に焦点を絞って書いていきます。
まずdbのモックを作成する際にsqlmock.New()
で作成できます。
引数なしで、New()
した時は、SQLの実測値と期待値の比較に正規表現を使用したものをデフォルトで使用するようになっています。
DATA-DOG/go-sqlmockが提供するQueryMatcherEqual
をオプションで使用したら実測値と期待値の比較は文字列として完全一致しているかを比較します。
※空白は削除された状態で比較されます。
実装例は以下のようになります。
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
また、自身でカスタマイズしたSQLの比較処理を使用することができます。
その際とても拡張性に優れていていいなと思ったのが、記事に残そうとしたきっかけです。
今回対象のソースコードと補足説明
不要な箇所は割愛して抜粋しています。
ソースコードと解説を交えて説明していきます。
driver.go
New()
で生成する際に可変長引数でoptionを受け取ります。
func New(options ...func(*sqlmock) error) (*sql.DB, Sqlmock, error) {
return smock.open(options)
}
sqlmock.go
for
文で引数で受け取ったoptionsは関数なので、*sqlmockを引数に実行しています。
あとで中身の説明はしますが、c.queryMatcherを設定しているイメージです。
なので、optionsに何も指定がない時は、c.queryMatcher = QueryMatcherRegexp
とあるように、
デフォルトのSQLの比較処理を指定しています。
func (c *sqlmock) open(options []func(*sqlmock) error) (*sql.DB, Sqlmock, error) {
for _, option := range options {
err := option(c)
if err != nil {
return db, c, err
}
}
if c.queryMatcher == nil {
c.queryMatcher = QueryMatcherRegexp
}
return db, c, db.Ping()
}
sqlmockの構造体の中身です。
type sqlmock struct {
ordered bool
dsn string
opened int
drv *mockDriver
converter driver.ValueConverter
queryMatcher QueryMatcher // 比較用インタフェースを埋め込み
expected []expectation
}
options.go
上記のNew()
で受け取る引数です。以下のように
sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
また引数で受け取ったqueryMatcher
をs *sqlmock
にsetしているのがわかります。
s *sqlmock
はsqlmock.goのoption(c)
で受け取ります。
func QueryMatcherOption(queryMatcher QueryMatcher) func(*sqlmock) error {
return func(s *sqlmock) error {
s.queryMatcher = queryMatcher
return nil
}
}
query.go
少し長いのでソースコード上にコメントを書いておきました。
比較の仕方を自分でカスタムしたいときは、QueryMatcherFunc型を実装した変数を作成し、
QueryMatcherOption
の引数に渡してあげるだけで使えます。
type QueryMatcher interface {
Match(expectedSQL, actualSQL string) error
}
// 関数を返す型:func(expectedSQL, actualSQL string) error
type QueryMatcherFunc func(expectedSQL, actualSQL string) error
// QueryMatcherの実装。関数を実行しているだけ
func (f QueryMatcherFunc) Match(expectedSQL, actualSQL string) error {
return f(expectedSQL, actualSQL)
}
/*
QueryMatcherFunc型を返す実装/QueryMatcherRegexp/QueryMatcherEqual
*/
// デフォルトで使う正規表現の比較
var QueryMatcherRegexp QueryMatcher = QueryMatcherFunc(func(expectedSQL, actualSQL string) error {
expect := stripQuery(expectedSQL)
actual := stripQuery(actualSQL)
re, err := regexp.Compile(expect)
// 比較処理・・・
return nil
})
// 空白を削除した文字列の一致で比較。sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)と書くことで使う。
var QueryMatcherEqual QueryMatcher = QueryMatcherFunc(func(expectedSQL, actualSQL string) error {
expect := stripQuery(expectedSQL)
actual := stripQuery(actualSQL)
// 比較処理・・・
return nil
})
open-closedの原則
golangのソースにopen-closedの原則が多くみらる原則の1つだと感じました。
インタフェースを用いることで、既存のソースに手を加える事がなく、拡張できるのでとても使い勝手がいいです。
詳細は以下の記事を参考
Go言語で再考するオープンクローズドの原則
最後に
理解するまで時間がかかりましたが、分かってくると拡張性に優れていて素晴らしいコードだなーと思えるようになりました。
実際に自分が実装する時もOSSから学んだことを活かして実装していきたいです。
Author And Source
この問題について(【OSSから学ぶ】Golang製ライブラリDATA-DOG/go-sqlmockを読んだ), 我々は、より多くの情報をここで見つけました https://qiita.com/yoshinori_hisakawa/items/11e508e5944bd82921da著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .