Traffine I/O

日本語

2022-12-30

dbtのテスト

はじめに

dbtではモデルをテストする機能があります。テストは品質の低いデータの混入を検知したり、データの品質を保つために使われます。

テストを定義する方法は次の2種類があります。

  • Singularテスト
  • Genericテスト

テストを定義し、dbt testコマンドを実行するとテストが実施されます。

Singular テスト

Singularテストではdbt_project.ymltest-pathsに指定したディレクトリ(デフォルトでは/tests)に.sqlファイルとしてテスト用のコードを記述します。

テストコードは存在してはいけない行を検索するSELECT文を記述します。

例えば、fct_paymentsというモデルがあり、amountがポジティブの値であるかどうかをテストしたい場合は次のようなSQLを記述します。

tests/assert_amount_is_positive.sql
-- Refunds have a negative amount, so the total amount should always be >= 0.
-- Therefore return records where this isn't true to make the test fail
select
  order_id,
  sum(amount)
from {{ ref('fct_payments')}}
group by 1
having not(amount >= 0)

dbt testコマンドを実行するとテストコードが実行され、行が検索されてしまった場合はテストが失敗します。テストが失敗すると次のようになります。

$ dbt test
14:19:07  Running with dbt=1.0.4
14:19:07  Found 1 model, 1 test, 0 snapshots, 0 analyses, 165 macros, 0 operations, 0 seed files, 2 sources, 0 exposures, 0 metrics
14:19:07
14:19:07  Concurrency: 1 threads (target='dev')
14:19:07
14:19:07  1 of 1 START test assert_amount_is_positive........................ [RUN]
14:19:07  1 of 1 FAIL 1 assert_amount_is_positive............................ [FAIL 1 in 0.05s]
14:19:07
14:19:07  Finished running 1 test in 0.23s.
14:19:07
14:19:07  Completed with 1 error and 0 warnings:
14:19:07
14:19:07  Failure in test assert_fct_payments (tests/assert_amount_is_positive.sql)
14:19:07    Got 1 result, configured to fail if != 0
14:19:07
14:19:07    compiled SQL at target/compiled/my_dbt_proj/tests/assert_amount_is_positive.sql
14:19:07
14:19:07  Done. PASS=0 WARN=0 ERROR=1 SKIP=0 TOTAL=1

テストに成功すると次のようになります。

$ dbt test

14:21:33  Running with dbt=1.0.4
14:21:33  Found 1 model, 1 test, 0 snapshots, 0 analyses, 165 macros, 0 operations, 0 seed files, 2 sources, 0 exposures, 0 metrics
14:21:33
14:21:33  Concurrency: 1 threads (target='dev')
14:21:33
14:21:33  1 of 1 START test assert_amount_is_positive....................... [RUN]
14:21:33  1 of 1 PASS assert_amount_is_positive............................. [PASS in 0.05s]
14:21:33
14:21:33  Finished running 1 test in 0.23s.
14:21:33
14:21:33  Completed successfully
14:21:33
14:21:33  Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1

Generic テスト

Genericテストでは、dbtが標準で用意している4つのスキーマテストを使ってテストします。

  • unique
    カラムがユニークであるかどうか
  • not_null
    カラムにNULL値が存在しないかどうか
  • accepted_values
    カラムの値が指定した値になっているかどうか
  • relationships
    参照整合性を満たしているかどうか

例えば次のように.ymlファイルに記述します。

models/schema.yml
version: 2

models:
  - name: customers
    columns:
      - name: customer_id
        tests:
          - unique
          - not_null

  - name: stg_customers
    columns:
      - name: customer_id
        tests:
          - unique
          - not_null

  - name: stg_orders
    columns:
      - name: order_id
        tests:
          - unique
          - not_null
      - name: status
        tests:
          - accepted_values:
              values: ['placed', 'shipped', 'completed', 'return_pending', 'returned']
      - name: customer_id
        tests:
          - not_null
          - relationships:
              to: ref('stg_customers')
              field: customer_id

dbt testを実行すると上記のymlファイルの内容のテストが走ります。

また、このスキーマテストは自身で拡張することも可能で、dbt-utils といった外部パッケージを利用することで拡張します。

カスタム Generic テスト

標準のGenericテストでは機能が不足している場合、カスタムGenericテストを作成することができます。

カスタムGeneric testではtests/genericディレクトリもしくはmacros/ディレクトリにtestブロックを使ったSQLファイルを記述します。

tests/generic/test_is_even.sql
{% test is_even(model, column_name) %}

with validation as (
    select
        {{ column_name }} as even_field
    from {{ model }}
),
validation_errors as (
    select even_field
    from validation
    where (even_field % 2) = 1
)
select *
from validation_errors

{% endtest %}

データモデルのファイルと同じ階層に.ymlファイルを作成し、次のようにtestsセクションにテスト名を記述します。

models/schema.yml
version: 2

models:
  - name: users
    columns:
      - name: favorite_number
        tests:
          - is_even

参考

https://docs.getdbt.com/docs/build/tests
https://zenn.dev/foursue/books/31456a86de5bb4/viewer/5efa91

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!