はじめに:テストは大事だが、書くのが面倒
データ変換のテストが大事だということに、反対する人はほとんどいないと思います。集計の途中でNULLが混ざっていたり、本来ユニークなはずのキーが重複していたりすると、その先のレポートやダッシュボードはすべて静かに狂っていきます。しかも、こういう不具合は数字がそれっぽく出てしまうぶん、気づくのが遅れがちです。
それでも、テストはなかなか書かれません。理由はシンプルで、面倒だからです。分析の本筋はSQLを書いて数字を出すことであって、「この列は本当にNULLでないか」を確認するコードを別に用意するのは、どうしても後回しになります。中小ECの現場で一人でデータ基盤を回していると、なおさらそうです。
この記事では、dbtの標準的なテスト機能を確認したうえで、Claude Codeを使ってモデルからテストコードを下書きする流れを紹介します。「面倒だから書かない」を「とりあえず下書きはある」に変えるのが狙いです。
なお、dbtでデータパイプラインを組む話はdbt × BigQueryで再現可能なデータパイプラインを構築する入門で、テストという考え方そのものはBigQueryでデータ品質をSQLテストで守るで扱っています。あわせて読むと、この記事の位置づけがわかりやすくなります。
dbtのテストはどんなものか
dbtのテストは、大きく分けて2種類あります。スキーマに紐づく汎用テスト(generic test)と、個別のSQLで書く単体テスト(singular test)です。
schema.ymlに書く汎用テスト
いちばんよく使うのが、schema.yml(モデルの定義ファイル)に書く汎用テストです。代表的なものが4つあります。
not_null:その列にNULLが含まれていないことunique:その列の値が重複していないことaccepted_values:その列が決められた値の集合に収まっていることrelationships:別テーブルのキーと参照整合性が取れていること
たとえば、注文を1行1注文にまとめたstg_ordersというモデルがあるとします。これに対するテストは、次のように書きます。
version: 2
models:
- name: stg_orders
description: "注文を1行1注文に整形したステージングモデル"
columns:
- name: order_id
description: "注文ID(主キー)"
tests:
- not_null
- unique
- name: order_status
description: "注文ステータス"
tests:
- accepted_values:
values: ["pending", "paid", "shipped", "cancelled"]
- name: customer_id
description: "顧客ID"
tests:
- not_null
- relationships:
to: ref('stg_customers')
field: customer_id
このYAMLが意味するところはとても素直です。「order_idはNULLでなく重複もない主キー」「order_statusは4つの値のどれか」「customer_idはNULLでなく、stg_customersに存在する顧客を指している」。これだけで、注文データの素性をかなり守れます。
書いたら、ターミナルでテストを実行します。
dbt test --select stg_orders
accepted_valuesに想定外のステータスが混ざっていたり、order_idに重複があったりすると、その時点でテストが失敗して教えてくれます。
singular testで個別のロジックを検証する
汎用テストでカバーしきれない、業務固有のルールもあります。たとえば「割引後の金額は、元の金額を超えてはいけない」のような条件です。こういうときは、tests/ディレクトリにSQLファイルを置く単体テスト(singular test)を使います。
dbtの単体テストは、「失敗する行を返すSQL」を書くという発想です。1行も返ってこなければ合格、何行か返ってくれば、その行が違反しているということになります。
-- tests/assert_discount_not_exceed_gross.sql
-- 割引後金額が元金額を上回っている行を返す(0行なら合格)
select
order_id,
gross_amount,
net_amount
from {{ ref('stg_orders') }}
where net_amount > gross_amount
この考え方に慣れると、「おかしい状態を一つのSQLで言い表す」だけでテストになるので、複雑なビジネスルールも素直に書けます。
Claude Codeでモデルからテストを自動生成する
ここからが本題です。テストの書き方そのものは難しくありませんが、すべてのモデルに対して列ごとに考えるのは、やはり骨が折れます。そこをClaude Codeに下書きさせます。
Claude Codeはターミナルで動くので、dbtプロジェクトのディレクトリでそのまま起動できます。モデルのSQLファイルを読ませて、テストの叩き台を出してもらう、という流れです。
まずモデルを読ませて意図を伝える
たとえばmodels/staging/stg_orders.sqlがあるとして、次のように指示します。
models/staging/stg_orders.sql を読んでください。
このモデルに対する schema.yml のテストを下書きしてほしいです。
- 主キーらしき列には not_null と unique を付ける
- ステータス列など値が限られそうな列は accepted_values の候補を挙げる
- 他テーブルを参照していそうな列は relationships の候補を挙げる
確証が持てない箇所は、断定せずコメントで「要確認」と書いてください。
Claude CodeはSQLの中身(selectしている列、joinの相手、case式で出している区分など)を読み取って、それらしいschema.ymlを提案してくれます。先ほどのstg_ordersの例のようなYAMLが、ほぼそのまま出てくることもあります。
業務ルールはsingular testとして相談する
汎用テストだけでなく、業務固有のチェックも相談できます。
このモデルで成り立っているべき業務ルールを3つ挙げて、
それぞれを dbt の singular test(失敗する行を返すSQL)として
tests/ ディレクトリ用に書いてください。
「金額は負にならない」「キャンセル注文に出荷日が入っていない」といったルールを、SQLの形まで落として提案してくれます。自分では見落としていたチェック観点に気づけることもあり、レビューのきっかけとしても役立ちます。
生成されたテストは、必ず人がレビューする
ここはこの記事でいちばん強調したいところです。Claude Codeが出すテストは、あくまで下書きです。そのまま信じてdbt testを緑にすることが目的ではありません。
特に次の点は、人が必ず確認してください。
accepted_valuesの値が本当に正しいか(実データには想定外の区分が紛れていることがあります)uniqueを付けた列が、本当にユニークであるべきか(実は重複が正常なケースもあります)relationshipsの参照先が、業務的に正しい関係か- 生成されたsingular testが、「正しい状態」ではなく「自分のSQLの実装」をなぞっただけになっていないか
最後の点は要注意です。モデルのSQLをそのまま材料にしてテストを作ると、「実装が正しい前提で、実装どおりかを確認するだけ」のテストになりがちです。これだと、実装の間違いはすり抜けてしまいます。テストは「あるべき状態」を表すべきで、その判断は人の仕事です。
生成物をいったん疑い、実データに対して走らせて挙動を見て、おかしな失敗・おかしな合格がないかを確かめる。この一手間を省かないことが、自動生成と付き合ううえでの肝になります。
運用に乗せる
下書きを人がレビューして取り込んだら、あとは普段の開発フローに組み込みます。難しいことはありません。
dbt build(実行とテストをまとめて行うコマンド)を日々のパイプラインに入れる- モデルを直したら、関連するテストも見直す習慣をつける
- 新しいモデルを足したときは、まずClaude Codeに下書きを作らせ、人がレビューしてから採用する
こうしておくと、テストが「特別な作業」ではなく、モデルとセットで増えていく当たり前のものになります。テストの初稿のハードルが下がるぶん、「とりあえず書いておくか」が現実的な選択肢になるのが、この組み合わせのいちばんの効きどころです。
まとめ
テストが大事なのはわかっていても、ゼロから書くのは面倒で、後回しになりがちです。dbtにはnot_null・unique・relationships・accepted_valuesといった汎用テストと、SQLで自由に書けるsingular testがそろっていて、データ変換の素性を守る土台は十分にあります。
そこにClaude Codeを足すと、モデルからテストの下書きを起こす部分を肩代わりさせられます。ただし生成物は下書きであり、accepted_valuesの値やuniqueの前提、singular testが実装をなぞっただけになっていないかは、人が必ずレビューしてください。
「面倒だから書かない」を「下書きはあるからレビューして取り込む」に変える。地味ですが、データ品質を継続的に守るうえで効いてくる進め方です。dbtのパイプライン自体の組み方はdbt × BigQueryで再現可能なデータパイプラインを構築する入門を、テストという考え方の基礎はBigQueryでデータ品質をSQLテストで守るを参照してみてください。