Goにおけるデータベース操作とテスト
Goでデータベースを操作する際には、標準パッケージであるdatabase/sql
、GORM、entなどの様々な選択肢が存在します。多くのライブラリではGoのコードを定義してSQLを生成しますが、sqlcはSQLをコンパイルしてGoのコードを生成するのが特徴のライブラリです。
このアプローチには、最終的に実行されるSQLが明らかであることやデータベースとやりとりするためのデータ構造を自分で定義する必要がないことといったメリットがあります。また、コンパイル時にSQLを解析し型や引数名の間違いを検出できます。そしてなにより、非常にシンプルです。
本記事では、sqlcの一歩進んだ使い方としてdockertestと組み合わせたテストの書き方について紹介します。dockertestとは、Dockerコンテナを立ち上げてテストを実行するための使いやすいコマンドを提供するパッケージです。Webアプリケーションのテストを行う際に、dockertestを利用することでデータベースをDockerコンテナとして用意できます。
なお、sqlcは現在MySQL、PostgreSQLとSQLiteをサポートしていますが、本記事ではPostgreSQLを使って解説します。
sqlcにはブラウザ上で気軽に振る舞いを確かめられるplaygroundがあります。適宜使用してみてください。
sqlcの使い方
基本的な使い方
まず、sqlcの特徴をざっくりと掴むために簡単なスキーマ定義と、書き込み読み込みを行うような例を紹介します。
sqlcではスキーマ設定ファイルschema.sql
とクエリ定義ファイルquery.sql
の2つのファイルをもとにコード生成を行います。今回準備したファイルは以下のとおりです。
この例では、ユーザーの参照と追加、そしてデータベースに保存されている年齢の更新を行うことができます。
sqlcではデータベースを操作する関数を定義するために、query.sql
の例のようにクエリに特定のコメント(-- name: CreateUser :one
)を追加します。ここではname:
のあとに続くのが関数名であり、:one
や:exec
が関数の種類を示しています。例えば、:one
ならばクエリからの返り値を一つ持つことを示しています。
ファイル構成は以下のようになっています。sqlc.yaml
にはoutputディレクトリに出力するように設定しました。公式ドキュメントのチュートリアルも是非参考にしてください。
├── query.sql
├── schema.sql
├── sqlc.yaml
└── output
├── db.go
├── models.go
└── query.sql.go
sqlc generate
を実行してコード生成を行った結果は以下のようになります。
playgroundからもこの結果を確認できます。
データベースの構造がmodel.go
にマッピングされ、データベースを操作する関数がquery.go
にQueries
のメソッドとして定義されています。sqlc はコード生成時に、SQLファイルをコンパイルしています。例えば、GetUser
の引数のid int32
のように、sqlcはスキーマのファイルから推論を行って型を設定しています。
また、db.go
に出力されたインターフェースDBTXをdatabase/sql
パッケージの*sql.DB
やpgx
パッケージの*pgx.Conn
は満たしているので、データベースとの接続を行うライブラリの選択肢も豊富です。
トランザクション
sqlcを使ってデータベースの簡単な操作が行えることは確認できました。一歩進んだ項目として、sqlcではトランザクションを利用した処理を書くこともできるので見てみましょう。
sqlcではWithTX
メソッドを利用することで、クエリをトランザクションの中で実行できます。あるユーザーの年齢をデータベースから読み出した後、1つ加算して更新する例を考えてみます。
この例ではGetUser
でユーザーの年齢を読み出し、UpdateUserAges
で年齢を更新しています。conn.Begin(ctx)
で発行したトランザクションはtx.Commit(ctx)
でコミットされます。
このように、トランザクションも簡単に実装できます。
sqlc+dockertest でデータベースを使ったテスト
Goにはuber-go/mock等の優れたパッケージがあり、アプリケーションのテストを書く際にデータベースをモックする場合も多いです。しかし、本番とできるだけ同じ条件でテストを動かしたい場合や、モック自体のメンテナンスコストが課題となることもあります。
この記事ではdockertestを使ってテストコードからPostgreSQLのDockerコンテナを立ち上げ、sqlcと組み合わせたテストの例を紹介します。
TestMain
では、dockertestを利用してpostgresのDockerコンテナを起動しています。docker-entrypoint-initdb.d
にスキーマファイルをマウントすると、コンテナ起動時に反映されます。TestUpdateUserAgesWithTransaction
では前節で紹介した年齢を1つ増やす関数をテストしています。
設定ファイルなどの記載は必要なく、テストの実行もとても簡単です。
% go test
PASS
ok dockertest-sqlc-test-sample 3.307s
この記事で紹介した例はこちらのリポジトリからも参照いただけます。
おわりに
この記事では、Go言語でのデータベース操作とテストに焦点を当て、sqlc
とdockertest
を組み合わせたテスト手法について紹介しました。データベースとのやりとりをシンプルかつ堅牢に行うためのsqlc
、そして本番環境にできるだけ近い状態でテストを行うためのdockertest
は、非常に強力なツールです。
テストはアプリケーションの品質を確保するために不可欠であり、本記事で紹介した手法では、実際のデータベースを利用したテストを容易に行うことができます。これによ本番環境と同じ条件での挙動をテストすることができます。
是非、これらのツールを活用して、より信頼性の高いソフトウェアを構築してください。