APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

Databricks-06. [Databricks × dbt] モデルに対するテストについて

はじめに

GLB事業部Lakehouse部の阿部です。
本記事では、dbt cloudで作成したモデルのテストについて解説します。
前回の記事では、Partner Connectを使用してDatabricks Lakehouse Platformからdbt Cloudに接続し、Databricksにあるデータをdbt cloud上で変換する流れについて解説しました。

techblog.ap-com.co.jp

前回の記事で作成したパイプライン全体に対してモデルのテストを実行し、最後にドキュメントサイトから実行したテストを確認したいと思います。

目次

dbtにおけるテストとは

dbtではモデルを始めとした各種リソース(Sources, Seeds, Snapshots)のテストが可能であり、すべてのリソースにテストするべきです。
dbtでのテスト方法は以下の3つです。

- Singular tests
- Genetic tests
- パッケージからテストモジュールを読み込んでテストする

テストモジュールは多くあるため本記事では紹介しませんが、今後検証してご紹介したいと思います。
ここでは、Singular testsとGenetic testsの2つについてそれぞれ解説していきます。

Singular tests

Singular testsは、SELECT文を書いて簡単にテストしたいときに実行します。
テストには.sqlファイルを使用し、dbt_project.ymltest_pathsに指定したディレクトリ(通常はtestsディレクトリ)配下に置きます。
dbt_project.ymlファイルの書き方については前回記事を参考)

そのため、testsディレクトリ配下にtest_sample.sqlファイルを用意し、Singular testのクエリーを記述します。

たとえば、stg_patient_allergiesモデルを参照してstart_dateカラムの日付はすべて現在よりも過去かどうかテストしたいとします。
この時に重要なことは、Singular testはレコード数が0になるクエリーを書くことです。
具体的にSIngular testが失敗する場合と成功する場合に分けて解説していきます。

テストが失敗するクエリーの例を先に示します。

select start_date
from {ref{stg_patient_allergies}}
where start_date < current_date 

where句を使用してstart_dateが現在よりも過去になるレコードを参照するクエリーを書きました。
過去の時系列データを扱うため、start_dateは現在よりも過去になるはずです。

コンソールにtestコマンドを入力してテストを実行します。
testコマンドは、dbt test --models stg_patient_allergies --select test_sampleと入力します。
modelsの後にテストするモデル名、selectの後に.sqlファイル名を指定します。

実行します。

ご覧のようにtest_sampleのテストは失敗しました。
理由として、指定したwhere句の条件ではレコード数が0にはならないからです。

次に、成功するクエリーを示します。

select start_date
from {ref{stg_patient_allergies}}
where start_date > current_date 

先程と同じクエリーに見えるかもしれませんが、変更箇所はwhere句の不等号の向きを変えただけです。
テスト結果の前にPreviewしてテーブルを参照します。

画像の通り抽出したレコード数は0であることがわかります。

それではtestコマンドを実行してテストが成功することを確認します。

テストが成功しました。

ここでSingular testsを使う場面をまとめまておきます。

- SELECT文を書いて簡単にテストしたいとき
- Generic tests(後述します)やパッケージに目的のテストが用意されておらず、テストをカスタマイズしたいとき

モデルやカラムは扱うデータによって変わることを考えると汎用性が低いため、基本的には多用せずに1つのモデルを簡単に、またはアドホック的にテストしたいときに書くことをオススメします。

Genetic tests

Singularテストと違い、多くのモデルに対して適用できる汎用性の高いテストをする場合に行います。

あらかじめ用意されているGenetic testについて以下に示します。
- unique: 指定したカラムのデータがユニーク(一意)であること
- not_null: 指定したカラムにnull(欠損値)を含まないこと
- relationships: 指定したテーブル間に存在する共通のカラムがあるかどうか(参照整合性があるかどうか)。
指定したカラムは外部キー(Foreign key)になります。 filedにはカラムを指定し、toで指定されたテーブル内のカラムを参照し、カラムの整合性を確認します。
- accepted_values: カラムの値が事前に定義した値のいずれかであることをテストします。
たとえば、あるカラムに"A"と"B"という値しか存在しないなら、その値を指定する。(今回は使用しません)

以降の章では、上記Genetic testを使用してテストを実行します。

Genetic testの前に

モデルをテストする前に、モデルごとに対応するGeneticテストとカラムについてまとめました。

今回は全てのパイプラインにテストしたいため、marts modelの子モデルを全てテストします。
まずはstg_patient_allergiesモデルのpatientカラムに欠損値がないかどうかテストします。
次に、stg_patient_careplansモデルのidカラムに欠損値がない、かつユニークであるかどうか、さらにpatientカラムに欠損値がないかテストします。
最後に、stg_patient_allergies_careplansモデルのidカラムに対してuniqueとnot_nullのテストを行い 、さらにstg_patient_careplansモデルのidカラムを参照し整合性をテストします。
また、patientカラムにnot_nullのテストをします。

前回までに作成したモデルを用いてテストを実行しますが、モデル作成のコードをこちらにも示します。
Partner Connectによるソースデータの準備は前回の記事を確認いただければと思います。

ソースデータのpatient.allergiesから抽出したモデルです。
テスト対象のカラムはpatientです。

with source_data as (
    select *
    from {{ source('patient', 'allergies')}}
)
, renamed_data as (                                 
    select cast(START as date)      as start_date
    , cast(STOP as date)            as stop_date
    , case when PATIENT like '%-%'
            then left(PATIENT, 8)
            else PATIENT
        end                         as patient
    , case when ENCOUNTER like '%-%'
            then left(ENCOUNTER, 8)
            else ENCOUNTER
        end                         as encounter
    , CODE                          as code
    , DESCRIPTION                   as description_allergiess
    from source_data
)

ソースデータのpatient.careplansから抽出したモデルです。
テスト対象のカラムはidとpatientです。

with source_data as (
   select *
     from {{ source('patient', 'careplans') }}
)

, renamed_data as (
    select case when Id like '%-%'
        then left(Id, 8)
        else Id
      end                           as id
    , cast(START as date)           as start_date
    , cast(STOP as date)            as stop_date
    , case when PATIENT like '%-%'
        then left(PATIENT, 8)
        else PATIENT
      end                           as patient
    , case when ENCOUNTER like '%-%'
        then left(ENCOUNTER, 8)
        else ENCOUNTER
      end  as encounter
    , CODE                          as code
    , DESCRIPTION                   as description_careplans
    from source_data
)

上記2つのモデルからjoinしたモデルです。
テスト対象のカラムは同じくidとpatientです。

with patient_allergies as (
  select *
    from {{ ref('stg_patient_allergies') }}
)


, patient_careplans as (
  select distinct id
  , start_date
  , patient
  , code
  , description_careplans
    from {{ ref('stg_patient_careplans') }}
)

, patient_allergies_careplans as (
    select patient_careplans.id
    , patient_allergies.start_date 
    , patient_allergies.patient
    , patient_allergies.code as allergies_code
    , patient_careplans.code as careplans_code
    , patient_allergies.description_allergiess
    , patient_careplans.description_careplans
    from patient_allergies
    inner join patient_careplans
    on patient_allergies.patient = patient_careplans.patient
)

モデルのテスト

モデルをテストするには、.ymlファイルにテストの内容を記述します。
models/staging/patientディレクトリにstg_patient.ymlという新しいファイルを作成し、これらモデルに対するテストコードを記述します。

version: 2
 
models:
  - name: stg_patient_allergies
    description: staging model that includes records of patient's allergies information
    columns:
      - name: patient
        description: not null patint id for each patient's allergies
        tests:
          - not_null
 
  - name: stg_patient_careplans
    description: staging model that includes records of patient's careplans information
    columns:
      - name: id
        description: unique & not null careplans id for patinet's careplans
        tests:
          - unique
          - not_null
      - name: patient
        description: not null patint id for each patient's careplans
        tests:
          - not_null

  - name: stg_patient_allergies_careplans
    description: staging model that combines records of allergies and care plans information
    columns:
      - name: id
        tests:
          - not_null
          - unique
          - relationships:
              to: ref('stg_patient_careplans')
              field: patient
      - name: patient
        description: not null patient id for patient's careplans
        tests:
          - not_null

こちらの.ymlファイルで示したテストコードの構成を説明します。
nameの下にcolumnsが来るように モデル名の次にカラム名を指定した後、実行するGenetic testの内容を書く流れとなります。
以下、それぞれのテストコードにおけるパラメーターについてです。

name: テーブルとしてmaterialization(実体化)したモデルを指定します。
columns: カラムの説明と実行するテストを設定します。
description: モデルの説明やカラムの説明、testパラメーターにテスト内容を記入します。

テストの対象カラムと内容は冒頭でふれたとおりです。

さて、いよいよテストを実行します。
今回は、最終的に作成したpatient_dataモデルに対してテストを実行します。
patient_data.sqlモデルは、前回の記事では以下のように作成しました。

{{
 config(
   materialized = "table",
   tags=["patient"]
 )
}}

with patient_data as (
  select *
    from {{ ref('stg_patient_allergies_careplans') }}
)

, risk_pred_data as (
    select id
    , patient
    , count(distinct allergies_code) as number_of_allergiess
    , count(distinct careplans_code) as number_of_careplans
    from patient_data
    group by 1, 2
    order by  number_of_allergiess desc, number_of_careplans desc
)

コンソールにテストを実行するコマンドを入力します。
テストコマンドはdbt test --select +patient_dataと入力します。
dbtでテストをする場合にdbt testと入力しますが、--selectの後にモデル名を指定することで指定のモデルのみテスト可能です。
さらに、モデル名の先頭に+記号を置くことで、そのモデルの親モデルをすべて選択してテストします。
つまり、patient_dataの親モデルである3つのモデルをすべてテストすることになります。
逆に、+記号がモデルの末尾に置かれた場合はそのモデルの子モデルをすべて選択するため、ソースデータに近い親モデルを指定することで子モデルをすべてテストできます。

+記号の付ける位置によるテストの違いについて詳しく知りたい方は、こちらを参考にいただければと思います。
Graph operators

以下は、コマンドの内容です:

テスト結果には、テストしたモデルとそれぞれにかかった時間が表示されます。
テストが成功したモデルはモデル名の横に緑のチェックマークが、失敗した場合は赤いチェックマークが表示されます。
全てのモデル名にチェックがついているため、パイプライン全体のテストは成功したことになります。

テストが成功したため、最後に本プロジェクトのドキュメントを作成して終了します。
作成したモデルの整理や共有を考えるとドキュメントの作成は重要です。
ドキュメントの作成はdbt docs generateコマンドで実行でき、実行後に画面左上のView docs(本のアイコン)をクリックすると、新規タブでドキュメントのサイトが起動します。
画面上の検索バーにモデル名を入力することで、先ほどテストしたモデルを探すことができます。
試しにstg_patientと入力するとモデルの候補が表示されます。

そこでまずは、stg_patient_careplansモデルを参照します。
カラム名やデータ型の右にテストコードに書いたDESCRIPTIONやTESTS列を確認できます。

TESTS列に書かれているローマ字は、実行したGenetic testのパターンの略です。
- N: Not null - U: Unique - F: Foreign key

stg_patient_allergies_careplansモデルの場合

idカラムがForeign keyであることが確認できます。

リネージュグラフも確認します。
画面右下のグラフアイコンをクリックすると表示できます。

ドキュメントを確認して実行したテストの内容が確認できました。

参考記事

おわりに

本記事では、前回の記事で作成したモデルに対してテストを実行し、ドキュメントサイトでテストの内容を確認できました。
今回はふれませんでしたが、dbt_utilsのパッケージにも様々なGenetic testが用意されており、目的のテストに対応できるようになっております。 次回は、モデルのデプロイについて解説したいと思います。

今後もDatabricksに関連した検証内容を投稿していきたいと思いますので、またご覧になっていただければと思います。

私たちはDatabricksを用いたデータ分析基盤の導入から内製化支援まで幅広く支援をしております。
もしご興味がある方は、お問い合わせ頂ければ幸いです。

www.ap-com.co.jp

また、dbt Labs, Inc. と販売パートナー契約を締結しており、dbtの販売と導入支援の提供が可能です。

www.ap-com.co.jp

そして、一緒に働いていただける仲間も募集中です!
APCにご興味がある方の連絡をお待ちしております。

www.ap-com.co.jp