dbt-datamocktoolを使ってテスト用の入出力ファイルを用いたモデルのロジックテストをしてみた

2023.12.15

データアナリティクス事業本部のueharaです。

今回は、dbt-datamocktoolを使ってテスト用の入出力ファイル(csv)を用いたモデルのロジックテストをしてみたいと思います。

はじめに

dbtで標準で備わっているテスト機能は、ユニーク性やバリデーションといったデータそのものの品質をテストするのが主な目的であるため、基本的にはモデルのロジックをテストする際には別のパッケージが必要になります

その中でも、今回はdbt-datamocktoolを利用してみたいと思います。

【2023/12/18 追記】

dbt-core v1.8 から単体テストが標準機能で提供されるようです。

Discussionsを眺めていると、csvファイルを使ったテストにも対応しているようです。

ディレクトリ構成

必要な部分だけ記載しますが、今回のディレクトリ構成は以下の通りです。

dbt_dev
├ models
|  ├ dev
|  | ├ my_model.sql               # 今回テスト対象のモデル
|  | └ schema.yml
|  └ raw.yml                      # sourceを記載
├ seeds
|  └ test_data
|   ├ test__expected_output.csv  # 今回のテスト用入力ファイル
|   └ test__raw_input.csv        # 今回のテスト用期待値の出力ファイル
├ dbt_project.yml
└ package.yml

package.yml

package.ymlには、依存パッケージとして今回利用する dbt_datamocktool を記載します。

package.yml

packages:
  - package: mjirv/dbt_datamocktool
    version: 0.3.5

dbt_project.yml

dbt_project.ymlは以下です。

dbt_project.yml

version: '1.0.0'
config-version: 2

profile: 'dbt_dev'

model-paths: ["models"]
analysis-paths: ["analyses"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]

clean-targets:
  - "target"
  - "dbt_packages"

seeds:
  dbt_dev:
    test_data:
      +schema: test

models:
  dbt_dev:
    dev:
      +materialized: table

dbt seed によってロードされる際、 test_data フォルダ以下のデータは test というカスタムスキーマでロードされるよう指定しています。

今回は target: dev に対して dev というスキーマを利用することを想定しているため、テストデータは dev_test というスキーマ配下にロードされることになります。

raw.yml

raw.ymlは以下です。

raw スキーマ配下に raw_input というテーブルがあることを想定します。

version: 2

sources:
  - name: staging
    database: dbt_dev
    schema: raw
    tables:
      - name: raw_input

my_model.sql

今回のテスト対象のモデル my_model は、あまり面白くは無いですが以下のようにしてみました。

my_model.sql

select
  id,
  data * 5 as data
from {{ source('staging', 'raw_input') }}

raw_input テーブルから iddata を取得し、 data に関してはその値を5倍にしています。

schema.yml

schema.ymlは以下です。

schema.yml

version: 2

models:
  - name: my_model
    tests:
      - dbt_datamocktool.unit_test:
          input_mapping:
            source('staging', 'raw_input'): ref('test__raw_input')
          expected_output: ref('test__expected_output')

my_model に対し、dbt_datamocktoolでのロジックテストを記載しています。

設定値に関しては、元のsourceのデータにテストデータである ref('test__raw_input') をマッピングし、期待する出力を指定しています。

テストデータのcsvファイル

入力と期待する出力のcsvファイルはそれぞれ以下の通りです。

test__raw_input.csv

id,data
1,10
2,20
3,30

test__expected_output.csv

id,data
1,50
2,100
3,150

先のロジックに従い、入力の data カラムに対し、出力の data カラムの値を5倍にしています。

実行確認

まず、依存パッケージのダウンロードのため以下のコマンドを実行します。

$ dbt deps

ダウンロードが完了したら、次にseedコマンドによりテストデータのロードを行います。

$ dbt seed

成功すると、以下の通り dev_test スキーマ配下にテストに利用するテーブルが作成されているかと思います。

ここまで準備ができたら、テストを実行してみます。

$ dbt test
...
09:18:06  1 of 1 START test dbt_datamocktool_unit_test_my_model_ref_test__expected_output___ref_test__raw_input_  [RUN]
09:18:06  1 of 1 PASS dbt_datamocktool_unit_test_my_model_ref_test__expected_output___ref_test__raw_input_  [PASS in 0.18s]
09:18:06  
09:18:06  Finished running 1 test in 0 hours 0 minutes and 0.28 seconds (0.28s).
09:18:06  
09:18:06  Completed successfully
09:18:06  
09:18:06  Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1

無事、入力とそれに対する期待値のテストデータを用いてテストがPASSできたことが分かります。

逆に、今度はあえてmy_modelの定義を変えて一部データがテスト結果と一致しないようにしてみます。

my_model.sql

select
  id,
  case id
    when 3 then data * 2
    else data * 5
  end as data
from {{ source('staging', 'raw_input') }}

上記は id カラムが3の時だけ data を5倍ではなく2倍にしています。

これで dbt test を実行すると、以下の通りエラーが出力されます。

$ dbt test
...
14:04:10  1 of 1 START test dbt_datamocktool_unit_test_my_model_ref_test__expected_output___ref_test__raw_input_  [RUN]
14:04:10  The test <> failed with the differences:
14:04:10  ================================================================
| id | data |  in_a |  in_b |
| -- | ---- | ----- | ----- |
|  3 |  150 |  True | False |
|  3 |   60 | False |  True |
14:04:10  ================================================================
14:04:10  1 of 1 FAIL 2 dbt_datamocktool_unit_test_my_model_ref_test__expected_output___ref_test__raw_input_  [FAIL 2 in 0.09s]
14:04:10  
14:04:10  Finished running 1 test in 0 hours 0 minutes and 0.17 seconds (0.17s).
14:04:10  
14:04:10  Completed with 1 error and 0 warnings:
14:04:10  
14:04:10  Failure in test dbt_datamocktool_unit_test_my_model_ref_test__expected_output___ref_test__raw_input_ (models/dev/schema.yml)
14:04:10    Got 2 results, configured to fail if != 0
14:04:10  
14:04:10    compiled Code at target/compiled/dbt_dev/models/dev/schema.yml/dbt_datamocktool_unit_test_my__ef049f60d2b7d5691e3da4484cbbf0e4.sql
14:04:10  
14:04:10  Done. PASS=0 WARN=0 ERROR=1 SKIP=0 TOTAL=1

id=3 のデータが期待値と一致していないことが読み取れます。

最後に

今回は、dbt-datamocktoolを使ってテスト用の入出力ファイル(csv)を用いたモデルのロジックテストをしてみました。

参考になりましたら幸いです。

参考文献