概要
RDBを使ったユニットテストをするときに、前提条件となるテストデータを用意することがある。
しかし外部キーの制約上、テストしたいことと直接関係ないデータの作成が必要になる状況が発生する。
例えば、テーブルが「親-子-孫」という階層になっているときに、孫に関するテストを書きたいのだが子だけでなく親のデータも必要になるといった感じ。
そういったときには、外部キーを一時的に無効化するという選択肢がある。
ALTER TABLEでTRIGGERを無効化する方法
TRIGGERを無効化することで、制約チェックを走らなくする。
テストデータの準備ができたら、再度有効化すればよい。
ALTER TABLE child_table DISABLE TRIGGER ALL;
ALTER TABLE child_table ENABLE TRIGGER ALL
JavaのDbSetupを使うなら下記のような感じになる。
これをユニットテストのsetupで行うイメージ。
Operation operation = Operations.sequenceOf(
Operations.sql("ALTER TABLE child_table DISABLE TRIGGER ALL"),
Operations.insertInto("child_table")
.columns("id", "parent_id", "name")
.values(11, 1, "1の子どもです")
.build()
, Operations.sql("ALTER TABLE child_table ENABLE TRIGGER ALL")
)
DbSetup(DataSourceDestination(dataSource), operation).launch()
dbsetup.ninja-squad.com
制約チェックをDEFERREDにする方法
PostgreSQLの制約チェックはデフォルトだとIMMEDIATE(即時)なのだが、これをDEFERRED(遅延)にする。
DEFERREDの場合、制約チェックはトランザクション終了時(コミット時)に走るようになる。
ただしDEFFERREDを使うにはそもそも制約をDEFERRABLEとして定義する必要がある。
CREATE TABLE child_table (
id SERIAL PRIMARY KEY,
parent_id INTEGER NOT NULL REFERENCES parent_table(id) DEFERRABLE,
name VARCHAR(128) NOT NULL
);
SET CONSTRAINTS ALL DEFERRED;
SET CONSTRAINTS ALL IMMEDIATE
無効化したいタイミングがトランザクション単位になるようであればこちらの方法もとれる。
しかしテストデータの作成とテストの実行はトランザクション単位を分けたほうがいいと思うので、ユニットテストで使うには微妙かも。
参考
stackoverflow.com
qiita.com