DATA-DOG/go-txdbでDB接続を含むテストを楽に書こう
DB接続を含むテストはツライ
- テスト用のデータをテストケースごとに用意しないといけない。
- DBの変更結果が他のテストケースに影響を与えないようにリセットしないといけない。
- DBへの変更が他のテストケースに影響を与えるので並列実行できない。
DATA-DOG/go-txdbを使うと改善できる
-
DATA-DOG/go-txdb で生成することのできるDBコネクションには↓のような特徴があります。
-
sql.DB
と互換性がある。
- すべてのクエリが独立したトランザクション内で実行される。
-
.Close()
を呼ぶとそのトランザクション内で実行されたクエリがすべてRollbackされる。
- これをうまく使うと、テストケースごとに独立したトランザクション内でクエリを実行することができ、テスト終了後にDB変更がRollbackされるので、テストケースごとのデータ処理が必要なくなり、他のテストケースへの影響もなくなるのでテストを並列実行することができます。
- 実際のサンプルコードは こちら に置いています。
軽い解説
pkg/dao/dao_test.go
- 今回はDB接続を含むコードを
dao
パッケージに置きます。
-
TestMain
を定義して、テストに必要なデータを go-testfixtures/testfixtures を使って挿入します。
-
testfixtures
は平行テストに対応していないことがREADMEにも書いてありますが、今回はTestMain
でしか呼ばれないので問題ありません。
- 後のテストで
txdb
を使うためにtxdb.Register
を呼んでおきます。
func TestMain(m *testing.M) {
prepare()
txdb.Register("txdb", "mysql", config.DB.DSN)
code := m.Run()
os.Exit(code)
}
func prepare() {
db, err := sql.Open(config.DB.Driver, config.DB.DSN)
if err != nil {
panic(err)
}
defer db.Close()
fixtures, err := testfixtures.New(
testfixtures.Database(db),
testfixtures.Dialect("mysql"),
testfixtures.Directory("/go/src/github.com/rinchsan/txdb-todo/testdata/fixtures"),
)
if err != nil {
panic(err)
}
if err := fixtures.Load(); err != nil {
panic(err)
}
}
pkg/dao/user_test.go
- ユーザ追加のテストを例に取ります。
- テスト対象のコードやDBのスキーマなどの詳細はGitHubのほうを見てください。
-
sql.Open
に渡すDriverに"txdb"
を指定して取得したコネクションを使ってテストをしていきます。
-
sql.Open
の第2引数に渡す文字列ごとに独立したトランザクションを作成してくれます。
-
defer
で呼んでいるdb.Close()
によってテストケースごとにDBの変更がRollbackされています。
-
t.Parallel()
を利用してテストを平行に走らせることも可能です。
- ちなみに今回の例では環境変数を利用することで、ローカル開発用に使っているデータベースとは別のものを使ってテストを行うようにしていますので、詳しくは
Makefile
のtest
を見てみてください。
func TestUserImpl_Add(t *testing.T) {
t.Parallel()
cases := map[string]struct {
user *entity.User
noErr bool
}{
"new user": {
user: &entity.User{Username: "rinchsan"},
noErr: true,
},
"duplicate username": {
user: &entity.User{Username: "John"},
noErr: true,
},
"empty username": {
user: &entity.User{Username: ""},
noErr: true,
},
}
for name, c := range cases {
c := c
t.Run(name, func(t *testing.T) {
t.Parallel()
db, err := sql.Open("txdb", uuid.New().String())
assert.NoError(t, err)
defer db.Close()
impl := dao.NewUser(db)
err = impl.Add(context.Background(), c.user)
if c.noErr {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
})
}
}
感想
-
DATA-DOG/go-txdb で生成することのできるDBコネクションには↓のような特徴があります。
-
sql.DB
と互換性がある。 - すべてのクエリが独立したトランザクション内で実行される。
-
.Close()
を呼ぶとそのトランザクション内で実行されたクエリがすべてRollbackされる。
-
- これをうまく使うと、テストケースごとに独立したトランザクション内でクエリを実行することができ、テスト終了後にDB変更がRollbackされるので、テストケースごとのデータ処理が必要なくなり、他のテストケースへの影響もなくなるのでテストを並列実行することができます。
- 実際のサンプルコードは こちら に置いています。
軽い解説
pkg/dao/dao_test.go
- 今回はDB接続を含むコードを
dao
パッケージに置きます。
-
TestMain
を定義して、テストに必要なデータを go-testfixtures/testfixtures を使って挿入します。
-
testfixtures
は平行テストに対応していないことがREADMEにも書いてありますが、今回はTestMain
でしか呼ばれないので問題ありません。
- 後のテストで
txdb
を使うためにtxdb.Register
を呼んでおきます。
func TestMain(m *testing.M) {
prepare()
txdb.Register("txdb", "mysql", config.DB.DSN)
code := m.Run()
os.Exit(code)
}
func prepare() {
db, err := sql.Open(config.DB.Driver, config.DB.DSN)
if err != nil {
panic(err)
}
defer db.Close()
fixtures, err := testfixtures.New(
testfixtures.Database(db),
testfixtures.Dialect("mysql"),
testfixtures.Directory("/go/src/github.com/rinchsan/txdb-todo/testdata/fixtures"),
)
if err != nil {
panic(err)
}
if err := fixtures.Load(); err != nil {
panic(err)
}
}
pkg/dao/user_test.go
- ユーザ追加のテストを例に取ります。
- テスト対象のコードやDBのスキーマなどの詳細はGitHubのほうを見てください。
-
sql.Open
に渡すDriverに"txdb"
を指定して取得したコネクションを使ってテストをしていきます。
-
sql.Open
の第2引数に渡す文字列ごとに独立したトランザクションを作成してくれます。
-
defer
で呼んでいるdb.Close()
によってテストケースごとにDBの変更がRollbackされています。
-
t.Parallel()
を利用してテストを平行に走らせることも可能です。
- ちなみに今回の例では環境変数を利用することで、ローカル開発用に使っているデータベースとは別のものを使ってテストを行うようにしていますので、詳しくは
Makefile
のtest
を見てみてください。
func TestUserImpl_Add(t *testing.T) {
t.Parallel()
cases := map[string]struct {
user *entity.User
noErr bool
}{
"new user": {
user: &entity.User{Username: "rinchsan"},
noErr: true,
},
"duplicate username": {
user: &entity.User{Username: "John"},
noErr: true,
},
"empty username": {
user: &entity.User{Username: ""},
noErr: true,
},
}
for name, c := range cases {
c := c
t.Run(name, func(t *testing.T) {
t.Parallel()
db, err := sql.Open("txdb", uuid.New().String())
assert.NoError(t, err)
defer db.Close()
impl := dao.NewUser(db)
err = impl.Add(context.Background(), c.user)
if c.noErr {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
})
}
}
感想
pkg/dao/dao_test.go
dao
パッケージに置きます。TestMain
を定義して、テストに必要なデータを go-testfixtures/testfixtures を使って挿入します。
-
testfixtures
は平行テストに対応していないことがREADMEにも書いてありますが、今回はTestMain
でしか呼ばれないので問題ありません。
txdb
を使うためにtxdb.Register
を呼んでおきます。func TestMain(m *testing.M) {
prepare()
txdb.Register("txdb", "mysql", config.DB.DSN)
code := m.Run()
os.Exit(code)
}
func prepare() {
db, err := sql.Open(config.DB.Driver, config.DB.DSN)
if err != nil {
panic(err)
}
defer db.Close()
fixtures, err := testfixtures.New(
testfixtures.Database(db),
testfixtures.Dialect("mysql"),
testfixtures.Directory("/go/src/github.com/rinchsan/txdb-todo/testdata/fixtures"),
)
if err != nil {
panic(err)
}
if err := fixtures.Load(); err != nil {
panic(err)
}
}
pkg/dao/user_test.go
- テスト対象のコードやDBのスキーマなどの詳細はGitHubのほうを見てください。
sql.Open
に渡すDriverに"txdb"
を指定して取得したコネクションを使ってテストをしていきます。
-
sql.Open
の第2引数に渡す文字列ごとに独立したトランザクションを作成してくれます。
defer
で呼んでいるdb.Close()
によってテストケースごとにDBの変更がRollbackされています。t.Parallel()
を利用してテストを平行に走らせることも可能です。Makefile
のtest
を見てみてください。func TestUserImpl_Add(t *testing.T) {
t.Parallel()
cases := map[string]struct {
user *entity.User
noErr bool
}{
"new user": {
user: &entity.User{Username: "rinchsan"},
noErr: true,
},
"duplicate username": {
user: &entity.User{Username: "John"},
noErr: true,
},
"empty username": {
user: &entity.User{Username: ""},
noErr: true,
},
}
for name, c := range cases {
c := c
t.Run(name, func(t *testing.T) {
t.Parallel()
db, err := sql.Open("txdb", uuid.New().String())
assert.NoError(t, err)
defer db.Close()
impl := dao.NewUser(db)
err = impl.Add(context.Background(), c.user)
if c.noErr {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
})
}
}
ビジネスロジックのテストをgomockとかを使って書くと、今回のtxdbと合わせて結構いい感じにプロジェクト全体のテストが書けそうです。
Author And Source
この問題について(DATA-DOG/go-txdbでDB接続を含むテストを楽に書こう), 我々は、より多くの情報をここで見つけました https://qiita.com/rinchsan/items/63dffe5a133b8cdb5117著者帰属:元の著者の情報は、元の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 .