はじめに
この記事は MySQL 8.0.26 をベースに記述しています
みなさんDBを使ったユニットテストをしていますか?
最近では少なくない「状況が許すならユニットテストでもモック・スタブじゃなくてリアルDB使えばいいじゃん」派の方は、DBを使ったユニットテストについて、以下のようなコードを書いていると思うのですが、たまにユニットテスト最中のDBを見たくなると思います。
try {
db.begin();
// テスト用データの準備
db.exec(`DELETE FROM test`);
db.exec(`INSERT INTO test VALUES (....)`)
testedMethod(); // ← ここをデバッグ実行とかで止めながらDBのデータをじっくり見たい!!
// データの検証
...
} finally {
// ユニットテスト中にいじったデータのロールバック
db.rollback();
}
もちろんユニットテストによるDB変更は最後にロールバックされるので、ユニットテスト中のDBの内容を見ようがありません。ロールバックのコードを一時的に取り除いたりで見ることはできますが、面倒な上にユニットテストで既存データが破壊されるので困りものです。
(まあそもそも品のいいエンジニアリングをしている人達はあまりこういうことをしたいとはならないのでしょうが……)
(MySQLの場合) そこで READ UNCOMMITTED
!
そういう時は READ UNCOMMITTED
分離レベルで繋ぎましょう。他のトランザクション(ユニットテスト)による変更をある程度見ることができます。
例えば以下はDBeaverによる分離レベルの設定画面です。 1
ユニットテスト接続の変更を見る際は、次のような流れになります。 ダーティーリード
の部分が今回の狙いです。
MySQL 8.0.26 で上記の動きを確認済みですが、将来のバージョンでダーティーリードが排除されたとしても不思議ではないし、トランザクション内容によってはダーティーリードが予想可能な形で発生するとは言えないので、あくまで現時点で通じるデバッグツールの一種ぐらいと捉えたほうが良さそうです。
(おまけ) MySQL (InnoDB) のトランザクション分離レベルについて
-
REPEATABLE READ
: デフォルト。トランザクション中に見える内容が基本的に他者起因で変わらない -
READ COMMITTED
: トランザクション中に他でコミットされた内容が見える -
READ UNCOMMITTED
: トランザクション中に他でコミットされていない内容も見える(ダーティーリード)。本記事で使っている分離レベル -
SERIALIZABLE
: 見た行全部ロック
詳しくは以下を参照してください。
PostgreSQLの場合はどうなのか?
PostgreSQLでは READ UNCOMMITTED
は READ COMMITTED
のように振る舞います。よってこの手法は使えません。残念!
PostgreSQL's Read Uncommitted mode behaves like Read Committed. This is because it is the only sensible way to map the standard isolation levels to PostgreSQL's multiversion concurrency control architecture.
まとめ
MySQLで READ UNCOMMITTED
分離レベルを使うと別トランザクションの変更を見ることができます。ユニットテスト中の最終的にロールバックされるデータベース変更を眺めるには便利なこともあるでしょう。
ちなみにPostgreSQLでも通用するようなユニットテスト中のトランザクションを覗く方法となると、ユニットテストのためだけのDBを専用で立てておき、データがユニットテストでいかに壊されようと問題がない状況とし、細かくユニットテスト中にコミットのコードを入れるぐらいしか方法が無いと思います。コード中で獲得しているデータベースコネクションを他ツールに分配するようなことができればいいのですがそのような方法は見つかりませんでした……
-
DBeaver の分離レベルのオプションは一度テスト接続しないと表示されないことに注意しましょう(空欄しか出なくてびっくりすると思うので) ↩