dbt Unit Tests について調べてみました
お久しぶりです。株式会社 Belong にて Data Platform チームに所属する Shuhei です。
弊社は dbt-core を用いて BigQuery にてデータ分析基盤を構築しています。
先日、dbt Unit Tests を試す機会があったため、その機能や使い方などをご紹介します。
dbt Unit Tests について
dbt の Unit Tests により、SQL モデルのロジックの静的テストを実施することができます。
Unit Tests は dbt Core v1.8 から利用可能になった機能です。まるでソフトウェア開発の単体テストのようなことが dbt の model に対して実施できるとのことだったので、早速試してみました!
Unit Tests と Data Tests の違い
その前に、Unit Tests と Data Tests の違いについて整理してみましょう。用語として似てはいますが、チェックする観点が少し異なるため、確認してみましょう。
ざっくり下記の表のように整理できるかと思います。
項目 | Unit Tests | Data Tests |
---|---|---|
目的 | モデルのロジックの正確性を検証する | データの品質や整合性を検証する |
対象 | モデルのクエリロジックや計算式 | 実際のデータセットの値 |
実行タイミング | モデルの開発時やビルド前 | モデルのビルド後 |
Unit Tests では、とあるモデルについて、ある入力値に対する期待出力値が得られるかどうかを確認することができます。
例えば、売上(revenue)
を計算するモデルを考えます。このモデルでは、price
と quantity
を掛け合わせて revenue
を計算します。
このとき、入力データが price: 100
, quantity: 2
のとき、出力データが revenue: 200
であることを確認したいとき、Unit Tests が役立ちます。

一方、Data Tests ではとあるモデルが保持する値について、期待するデータ品質および整合性が保たれているかを確認することができます。
例えば、出力データの revenue
が NUMERIC
型であることや NOT NULL
であることなどを確認したいとき、Data Tests が役立ちます。
Unit Tests のやり方
それでは実際に、Unit Tests を試してみましょう!必要なファイルは大きく 2 つです。
- Unit Tests を実行する対象の dbt model の SQL ファイル
- Unit Tests を定義した yaml ファイル
再掲となりますが、前述の 売上(revenue)
を計算するモデルを例に、Unit Tests を実装してみましょう。

SQL ファイル
当然ですが、Unit Tests 対象のモデルファイルが必要です。下記のように revenue
モデルを作成したと考えてみます。
WITH fct_sales AS (
SELECT
id,
price,
quantity
FROM {{ ref('fct_sales') }}
)
SELECT
id,
price,
quantity,
price * quantity AS revenue
FROM fct_sales
Unit Tests を行うために、dbt run
で revenue
モデルを作成しておきましょう。
dbt run --select revenue
yaml ファイル
続いて、revenue
モデルのロジックをテストすべく、yaml ファイルを作成します。
入力データが price: 100
, quantity: 2
のとき、出力データが revenue: 200
であることを確認したいとき、下記のように記述できます。
unit_tests:
- name: test_revenue_equals_price_times_quantity
description: revenue が price と quantity を掛け合わせた値であることを確認するテスト
model: revenue
given:
- input: ref('fct_sales')
rows:
- { price: 100, quantity: 2 }
# - 必要に応じて入力を追加
expect:
rows:
- { revenue: 200 }
# - 必要に応じて期待結果を追加
地味にうれしいポイントとして、rows には確認したいロジックに関わるカラムのみ指定することができます。この特徴は多くのカラムを持つモデルに対して非常に有効ですし、Unit Tests 1 つ 1 つの見通しもよくなりますね。
主要なパラメータについて簡単に説明しておくと、下記のように整理できます。
パラメータ名 | 説明 |
---|---|
name | テストの名称 |
description | テストの説明。テストの目的や意図を簡潔に記述します。 |
model | テスト対象のモデル名。テストが適用される dbt モデルを指定します。 |
given | テスト対象モデルに渡す入力データを定義します。データセットは、 input 配下に複数の要素を設定可能です。 |
input | モデルに渡す入力として参照される dbt モデルを指定し、受け取るデータを定義します。 |
rows | 指定した input モデルに対して実際に使用する行データを記述します。 |
expect | モデルの期待される出力データを定義します。テストが成功するためには、実行結果と一致する必要があります。 |
format | 行データのフォーマットを指定します。csv や sql を選択できます。 |
以上のファイルが作成できたら dbt test
コマンドで Unit Tests を実行することができます!
# 上述の Unit Tests にのみフィルタして実行する例
dbt test --select test_revenue_equals_price_times_quantity
※ソースのモデル fct_sales
も事前に作成しておく必要がありますので注意しましょう。作成していないと下記のようなエラーとなります。
Compilation Error in model fct_sales (models/test_revenue.yaml)
Not able to get columns for unit test 'fct_sales' from relation `XXX`.`YYY`.`fct_sales` because the relation doesn't exist
> in macro get_fixture_sql (macros/unit_test_sql/get_fixture_sql.sql)
> called by model fct_sales (models/test_revenue.yaml)
Unit Tests を使うことで、dbt model のロジックが要件を満たしているか確認しやすくなり、開発時やリファクタ時に重宝しそうだなと感じました!
仮に test に失敗したとしても、actual differs from expected
として、実際の結果と期待結果との差分もわかりやすく表示してくれるため、非常に便利だなと思いました。
ephemeral model に対して Unit Tests する
私が Unit Tests を使用してみたいと感じたのは、intermediate layer の ephemeral model に対してでした。
dbt model の見通しを良くするために、intermediate の model にロジックを集約させることは多いかと思います。したがって、intermediate の各 model に対して Unit Tests を実装することは、効果が高いのではないかと考えました。
複数の ephemeral model に依存する model の例

ephemeral な model は DWH 上に構築されるわけではなく、Model Contracts など一部の機能に対して制限がありますが、Unit Tests は ephemeral な model に対しても利用することができます。 (この点が少し不安でしたが杞憂に終わりました 😌)
ephemeral model に対する Unit Tests も、前述の例とほとんど使用方法は変わらないため yaml の詳細は省略します。
unit_tests:
- name: test_XXX_is_passed
description: ephemeral_model_1 の XXX ロジックのテスト
model: ephemeral_model_1
given:
- ...
ephemeral model を Mock する
前述の main_model
のように、 ephemeral model を参照するモデルはどのように Unit Tests すればよいでしょうか?少しクセがあるため、確認していきます。
revenue
の例では dict で input データを Mock していました。
given:
- input: ref('fct_sales')
rows:
- { id: 1, price: 100, quantity: 2 }
input データは format
により dict や csv が指定可能ですが、 ephemeral model では format: sql
を使用します。
unit_tests:
- name: test_revenue_equals_price_times_quantity
model: revenue
given:
- input: ref('ephemeral_model') # ephemeral model を Mock する
format: sql
rows: |
select 1 as id, 100 as price, 2 as quantity
# 必要に応じて UNION ALL 等で行を追加
expect:
rows:
- { id: 1, revenue: 200 }
sql は inline で記述するほか、ファイルに切り出することもできます。下記は tests/fixtures/mock_ephemeral_model.sql
に Mock データを SELECT する SQL ファイルを配置し、読み込ませている例となります。
unit_tests:
- name: test_revenue_equals_price_times_quantity
model: revenue
given:
- input: ref('ephemeral_model') # ephemeral model を Mock する
format: sql
fixture: mock_ephemeral_model # デフォルトでは tests/fixtures 配下を探索する
expect:
rows:
- { id: 1, revenue: 200 }
本稿執筆現在、公式ドキュメントによると、ephemeral model の Mock は format: sql
を指定しなければならないようです。私が確認した限りでは、format: dict
や format: csv
は使用できませんでした。
If you want to unit test a model that depends on an ephemeral model, you must use format: sql for that input.
まとめ
dbt の Unit Tests を活用することで、モデルのロジックの正確性を簡単にテストすることができることがわかりました。
Unit Tests を開発時に作っておけばロジックを検証しながら安心して開発が進められますし、リファクタ時にも役立ちそうだなと思いました!ロジックを構築したあと、model を dbt run してデータを確認し、ロジックが正しいか検算して... の大変な作業から解放してくれそうで、どんどんこれから使っていきたいです。
また、Unit Tests は ephemeral model 自身や、ephemeral model を参照する model にも活用することができるのも、とても使いやすいなと感じました!
現在、Unit Tests の input には dbt の model を指定可能ですが、CTEs の単位などで指定できるとより柔軟にテストできそうだなと思いました。
Belong ではこのように dbt を用いた DWH の構築を積極的に実施しています!Data Platform チームに興味を持っていただけた方は是非 Entrancebook をご覧ください!