テストを書いている中で、特にデータベース依存のコードのテストをコードってどうやって書いてますか?
インメモリデータベースやトランザクションを使ってやるなどの方法がありますが、今回はtxdbというライブラリを使って、簡単にデータベース依存のコードを書く方法を紹介したいと思います。
インメモリデータベースを使ったテストの記事
インメモリデータベースを使ってテストができるgo-sqlmock
の記事はこちらです。
txdbとは
データベース接続と同時にトランザクションを張ることができ、接続を切るとロールバックしてくれるGoのライブラリです。
テストの際にモックデータを入れてそのデータをクリーンアップしたい時とかに便利なライブラリです。
使い方
環境
今回はORMライブラリとしてgormを採用しています。
なのでgorm使用した場合のスクリプトを書いていますが、他のORMライブラリでもそんなに変わらないと思います。
セットアップ
txdbを使うためにgo.mod
のあるディレクトリで下のコマンドを入力します。
go get github.com/DATA-DOG/go-txdb
接続スクリプトを書く
txdbを使ってデータベースに接続することで、接続と同時にトランザクションを張ってくれるようになります。
接続スクリプトは下のようになっています。
type TestDB struct {
DB *gorm.DB
}
var dsn string
func init() {
user, password, host, port, dbName := "root", "root", "127.0.0.1", "3306", "report_generator_test"
if os.Getenv("DB_USER") != "" {
user = os.Getenv("DB_USER")
}
if os.Getenv("DB_PASSWORD") != "" {
password = os.Getenv("DB_PASSWORD")
}
if os.Getenv("DB_HOST") != "" {
host = os.Getenv("DB_HOST")
}
if os.Getenv("DB_PORT") != "" {
port = os.Getenv("DB_PORT")
}
dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true",
user,
password,
host,
port,
dbName,
)
txdb.Register("txdb", "mysql", dsn)
}
func NewTestDB() (*TestDB, error) {
var db *gorm.DB
var err error
dialector := mysql.New(mysql.Config{
DriverName: "txdb",
DSN: strconv.FormatInt(time.Now().UnixNano(), 10),
},
)
db, err = gorm.Open(dialector, &gorm.Config{})
if err != nil {
return nil, err
}
return &TestDB{DB: db}, nil
}
まず、init関数内でRegisterメソッドでtxdbのドライバーを登録します。
init関数はパッケージの初期化時に実行される関数です。
Registerメソッドでは、第一引数にtxdb用のドライバー名を設定します。第二引数にドライバー名を指定して、第3引数にdsn(DataSourceName)を指定します。
txdb.Register(name, drv, dsn)
次に、データベース操作をするためのインスタンスを受け取るためのインスタンスメソッドを実装しています。
func NewTestDB() (*TestDB, error) {
gormをデータベースに接続させるためにdialectorに接続情報を記述します。
先ほどtxdbように登録したデータベースドライバーをtxdb
と登録したので、DriverName
にはRegisterメソッドで登録した名前を記述します。
DSN
は、データベース接続に対する識別子として機能します。
時間をDSNとして登録することで効率的に識別子を生成することができます。
dialector := mysql.New(mysql.Config{
DriverName: "txdb",
DSN: strconv.FormatInt(time.Now().UnixNano(), 10),
},
dialectorを指定して、gormで操作するためのdbインスタンスを生成します。
db, err = gorm.Open(dialector, &gorm.Config{})
こうすることでtxdbを使いつつ、gormでのデータベース操作ができるようになります。
切断スクリプトを抽象化する
txdbでは切断することでロールバックしてくれるので、挿入したデータをない状態に戻してくれます。
単純にgormでラッピングしているsqlパッケージのdbインスタンスを取り出し、Closeメソッドを使うことで切断できますが、データを消去するために使うにしては大分わかりづらいので、データ削除用のメソッドとして実装しようと思います。
func (t *TestDB) TruncateTables() error {
db, err := t.DB.DB()
if err != nil {
return err
}
return db.Close()
}
sqlパッケージにあるDB型のインスタンスを取り出すために、DBメソッドを使用しています。
db, err := t.DB.DB()
そのインスタンスを使用してdb.Closeを実行してロールバックを行っています。
return db.Close()
テストを書いてみる
使い方の例として、簡略的なコードを書いてみます。
func TestCreate(t *testing.T) {
db, err := mysql.NewTestDB()
assert.NoError(t, err)
defer db.TruncateTables()
r := repository.New(db.DB)
// メソッドの呼び出し
res, err := r.Create(
ctx,
input,
)
}
先ほど実装したコードを使用してテストを行います。
まず、dbインスタンスをまず受け取ります。
db, err := mysql.NewTestDB()
assert.NoError(t, err)
そしてテストケースが終了した後にロールバック(クリーンアップ)するように、deferで該当メソッドを指定します。
defer db.TruncateTables()
あとはTestDB構造体の中にgormのインスタンスがあるので下のように取り出して、repository等データベース操作が実装されているパッケージのコンストラクタにDI(依存性注入)していくことで、テストをしていくことができます。
r := repository.New(db.DB)
かなり簡略的に上のは書いていますが、DIするdbインスタンスを用意する際にtxDB向けにセットアップしたgormのインスタンスを生成してDIします。
最後に
txDBを使うことで大分データベース依存のテストケースが書きやすくなります。
ぜひ使ってみてください!
Go関連の記事