Featured image of post GoでSQLのユニットテストを書く(dockertest)

GoでSQLのユニットテストを書く(dockertest)

GoからDocker経由でテスト用のDB(MySQL)を立ててテストした

まとめ

dockertestを使うとGoのコード内からテスト対象のDBをDockerコンテナとして起動して接続することができる.

これを使ってDBを都度立ち上げて, 実際にレコード操作の検証をするテストを書いてみた.

環境

やりかた

この前はDBをモックしたテストを試したけど,
やっぱり実際にテスト用のDBを立てて期待値が得られるか検証したいときもある.

テスト対象となるのは前回同様こんな感じの処理.

今回はGoからDockerをいじることができるdockertestを使ってテスト用DBを起動し,
それに接続してテストを行う.

まずはテーブル定義を記述したファイルをつくる.

ついでにMySQLの設定ファイルを作っておく.

次に公式の例1を参考に, MySQL2のコンテナを立ち上げる関数を書いてみる.

頑張ったのはcreateContainer()RunOptions3を使ってコンテナ起動時に細かいオプションを指定したり必要な設定ファイルをマウントできるようにしたところ.
こうしておくことで大体のDBの設定をこのテストコードに封じ込めることができる.

closeContainer()を呼び忘れるとテストが終わってもコンテナが残ってしまうので注意する(deferで呼んでおくのが良さそう).

各機能のテストではそれぞれこの関数を呼び出してテスト用のDBを作成する.

前回同様Create()についてテストを作るとこんな感じ.

一応テスト対象の関数以外のDB操作(挿入したレコードを取得するときなど)もRead()などの自分で定義している関数ではなく都度SQLを実行するようにした.

最終的なテストコードはこんな感じ.

最後にテストを実行してみる.

# ディレクトリの状態
$ tree .
.
├── go.mod
├── go.sum
├── main.go
├── main_test.go
├── my.cnf
└── todo.sql

# テスト実行
$ go test -v ./...
=== RUN   TestCreateWithDB
=== RUN   TestCreateWithDB/レコードが正しく追加されることのテスト
[mysql] 2021/05/13 23:41:36 packets.go:37: unexpected EOF
[mysql] 2021/05/13 23:41:36 packets.go:37: unexpected EOF
[mysql] 2021/05/13 23:41:36 packets.go:37: unexpected EOF
--- PASS: TestCreateWithDB (27.14s)
    --- PASS: TestCreateWithDB/レコードが正しく追加されることのテスト (27.14s)
=== RUN   TestReadWithDB
=== RUN   TestReadWithDB/レコードが正しく取得できることのテスト
[mysql] 2021/05/13 23:42:03 packets.go:37: unexpected EOF
[mysql] 2021/05/13 23:42:03 packets.go:37: unexpected EOF
[mysql] 2021/05/13 23:42:03 packets.go:37: unexpected EOF
--- PASS: TestReadWithDB (26.95s)
    --- PASS: TestReadWithDB/レコードが正しく取得できることのテスト (26.95s)
=== RUN   TestUpdateWithDB
=== RUN   TestUpdateWithDB/レコードが正しく更新できることのテスト
[mysql] 2021/05/13 23:42:30 packets.go:37: unexpected EOF
[mysql] 2021/05/13 23:42:30 packets.go:37: unexpected EOF
[mysql] 2021/05/13 23:42:30 packets.go:37: unexpected EOF
--- PASS: TestUpdateWithDB (27.15s)
    --- PASS: TestUpdateWithDB/レコードが正しく更新できることのテスト (27.15s)
=== RUN   TestDeleteWithDB
=== RUN   TestDeleteWithDB/レコードが正しく削除できることのテスト
[mysql] 2021/05/13 23:42:58 packets.go:37: unexpected EOF
[mysql] 2021/05/13 23:42:58 packets.go:37: unexpected EOF
[mysql] 2021/05/13 23:42:58 packets.go:37: unexpected EOF
--- PASS: TestDeleteWithDB (28.51s)
    --- PASS: TestDeleteWithDB/レコードが正しく削除できることのテスト (28.51s)
PASS
ok  	github.com/uzimihsr/golang-db-test	110.730s

コンテナを立ち上げてから疎通できるまでにちょっと時間がかかるのでunexpected EOFのエラーが何回か出てるけど, 内部でリトライをしてるので問題ないはず.

都度Dockerの操作をしてるせいでかなり時間がかかっているので, t.Parallel()4とかを使って並列実行させたほうが良いかも.

おわり

dockertestを使ってテスト用のMySQLコンテナをGoのコードから立てて操作してみた.

別にDBに限らず外部とのやりとりをする部分は対象をDocker化できるなら何でも使えちゃいそう.

ただしテスト環境でGo以外にDockerが必要になったり,
コンテナの立ち上げにかなり時間がかかるなどクセも強いのでどこまでやるかはテスト要件と相談して使いたい.

おまけ

かわいいねこ