株式会社renue
AI導入・DXの悩みをプロに相談してみませんか?
AIやDXに関する悩みがありましたら、お気軽にrenueの無料相談をご利用ください。 renueのAI支援実績、コンサルティングの方針や進め方をご紹介します。
dbt(data build tool) を実務で運用するチームが最初に詰まるのは「テストをどう書くか」ではなく「テストの責任範囲をどう分けるか」。schema test と data test を一緒くたに書くとテスト数が爆発し、CI が遅くなり、誰がどのテスト失敗を直すのかが曖昧になる。本記事では dbt のテスト戦略を、責任分離・運用負荷・失敗時の切り分けの3観点で整理する。
dbt テストは公式に「data tests」と「schema 配下のプロパティテスト」の2系統として扱われる。本記事ではこれをそれぞれ「schema test(構造制約)」「data test(値・業務ロジック制約)」と呼んで使い分け、設計の落とし穴と運用ノウハウを書く。
schema test と data test の境界線
schema test は「列の型・null性・一意性・外部キー整合」のようにモデル構造に関する制約を、yml ファイルでカラム単位に書く。dbt が標準で持つ not_null / unique / accepted_values / relationships の4種類は schema test の中心になる。Metaplane の dbt テストベストプラクティスでも、まずこの4種を主キー・外部キー・分析で参照されるディメンションに必ず当てるところから始めることが推奨されている。
一方の data test は業務ロジックに紐づく値の制約を、tests ディレクトリ配下に SQL ファイルとして書く。例えば「売上金額は注文行の単価×数量の合計に一致する」「クーポン適用後の値引き率は規定範囲に収まる」「キャンセル後の出荷ログは存在しない」といった、業務ルールに照らしたアサーションを記述する。data test は失敗した行を返すクエリとして書き、0行なら成功と判定される。
この2系統を混ぜると何が起こるか。schema test を yml に乱雑に書くと、モデルの carrier yml が肥大化して読めなくなる。逆に data test を yml にインライン化すると、複雑な SQL が yml ファイル内に閉じ込められ、レビューが進まない。 Elementary Data の dbt テストガイドでも、「テストを書きすぎるな、書く価値のあるテストを書け」という観点が繰り返し強調されている。
schema test の粒度設計
schema test を全カラムに書く運用は、初期コストは低く見えるが、運用に入ると負債になる。理由は2つ。第一に、ユニーク制約や null 制約は主キーや結合キーでのみ意味があり、説明用カラム(住所文字列、フリーテキストなど)には意味がない。第二に、accepted_values テストはマスター列に対しては効くが、マスターの値が増えるたびに yml を更新する手間が発生し、運用が止まる。
schema test の粒度設計の現実解は「主キー + 結合キー + 分析で参照するディメンションに限定する」。それ以外のカラムは、dbt のカスタム generic testでドメイン特化のチェックを書くか、Elementary や Metaplane のような外部監視ツールにデータ品質スキャンを委ねる設計が現実的。schema test と外部監視で役割分担すると、yml ファイルが肥大化せず、CI 時間も伸びない。
data test の運用設計と責任分離
data test が落ちたとき、誰が直すのかを yml の description に書く。これだけでチーム運用の停滞ポイントが1つ消える。失敗時に「データ提供元(上流ETL/ソース)の責任なのか」「dbt モデル(下流変換)の責任なのか」「テストの閾値設計(自分達)の責任なのか」が曖昧だと、Slack で議論が長期化する。
data test を書くときの実務テンプレートは、(a) 何を保証するテストか、(b) 失敗した場合に最初に疑う原因、(c) 失敗時の連絡先、を description に1〜2文で書くこと。さらに、dbt のstore_failures: trueを設定し、失敗した行を専用テーブルに保存しておくと、原因調査の手戻りが減る。失敗行が消えてしまうと「再現できなかったので無視」になりがちで、品質が下がる。
data test の運用で見落とされやすいのが、Datafold の dbt テスト運用論でも触れられている「severity 設定」。テストが落ちたときに warning にするか error にするかを、業務影響の重さで分ける。マスターデータ不整合は error で CI を止め、軽微な数値ずれは warning で次の朝会に持ち越す、という運用にすると、CI ストップが減って開発速度が落ちない。
モデル間依存とテスト実行順序
dbt の DAG が深くなると、上流モデルのテスト失敗が下流に伝播し、原因特定に時間がかかる。実務では dbt build コマンドを使ってモデルとテストを DAG 順に実行する設計が標準になっており、上流が失敗した時点で下流のモデル更新がスキップされる仕組みが用意されている。これにより、失敗の起点モデルを1つに絞れる。
CI 上では state:modified+ セレクタを使い、変更モデルとその下流だけを実行するのが現実解。フルランは夜間の cron で日次に1回回し、PR の CI では差分実行に絞る。dbt の公式ベストプラクティスでも、PR ごとのフルラン運用は規模が大きくなると CI 時間が支配的になるため、差分実行への移行が推奨されている。
モデル間の依存関係を強くしすぎると、テスト失敗が連鎖して原因特定が遅れる。staging 層・intermediate 層・mart 層の3層設計を徹底し、層をまたいだ参照を最小化すると、テスト失敗が起きた層を見ればおおよその責任範囲が見える。3層設計を緩めるとテストの保守コストが指数的に増える。
テストが詰まったときの切り分け手順
dbt test が失敗したとき、切り分け順序は次のとおり。第一にテスト自体の閾値設計を疑う。閾値の更新漏れは、コード変更や上流データ変更より発見しやすい。第二に上流データの変化を疑う。日次でデータ件数や代表値が大きく動いたら、ソース側のスキーマ変更や ETL 設定変更を確認する。第三にモデルロジックの変更を疑う。直前の PR で macro や intermediate モデルに触れたかを git log で確認する。
切り分けで最も時間を食うのは、compiled クエリを読まずにエラーメッセージだけで判断しようとする場面。target/compiled/ 配下に dbt が生成した実 SQL があるので、これを直接 BigQuery / Snowflake / Postgres に投げて値を確認するのが直接的な切り分け手段になる。compiled SQL を読む習慣がチームに根付くと、原因特定までの工数が下がる。
原因が「テスト自体の閾値設計」の場合、yml の description に閾値変更の経緯を残す。「以前の正常レンジが、キャンペーン施策の影響で新しい正常レンジに移った」のような業務文脈を残すと、半年後に同じテストで詰まったときに、再調査せずに済む。テスト yml を業務記録として運用することが、長期運用の鍵になる。
テスト数を増やしすぎないための運用ルール
dbt テストの落とし穴は、書きすぎることによるCI 遅延と保守コスト爆発。dbt データテストのパターン整理でも、テスト数を増やすほど品質が上がるわけではないという観点が指摘されている。テスト追加の判断基準は「失敗したら誰がいつ直すか」が答えられるか。答えられないテストは追加しない。
具体的なテスト追加ルール例: (a) 主キー・結合キーには必ず not_null + unique、(b) マスター連携カラムには relationships、(c) 業務ルールに紐づく値制約は data test、(d) その他のカラムは追加しない、外部監視に任せる。この4ルールを yml レビュー観点に組み込むと、テスト数の暴走を防げる。
dbt テスト戦略をチーム文化に落とす
dbt のテスト戦略は、技術的な書き方よりチームの運用文化に依存する。テストを書く責任、失敗を直す責任、閾値を見直す責任が誰のものかを明文化しないと、テストは「書いたきり」になり、品質保証として機能しない。dbt 導入の初期から「テスト所有者」をモデル単位で割り当てる運用にしておくと、長期運用での品質維持コストを抑えられる。
dbt の運用が長くなると、テストファイルが肥大化し、yml レビューに時間がかかるようになる。半年〜1年に一度、テスト棚卸しを入れて、使われていないテスト・閾値が古すぎるテスト・現業務に合っていないテストを削除する。テストを増やす運用だけでなく、削る運用を入れることで、テストが「資産」のまま運用される。中国語圏の dbt 運用整理でも、テストの追加と削除を半年単位で見直すというサイクル運用が紹介されており、地域を問わず長期運用での共通課題として扱われている。
