はじめに
GLB事業部Lakehouse部の阿部です。
本記事では、dbt cloudで作成したモデルのテストについて解説します。
前回の記事では、Partner Connectを使用してDatabricks Lakehouse Platformからdbt Cloudに接続し、Databricksにあるデータをdbt cloud上で変換する流れについて解説しました。
前回の記事で作成したパイプライン全体に対してモデルのテストを実行し、最後にドキュメントサイトから実行したテストを確認したいと思います。
目次
dbtにおけるテストとは
dbtではモデルを始めとした各種リソース(Sources, Seeds, Snapshots)のテストが可能であり、すべてのリソースにテストするべきです。
dbtでのテスト方法は以下の3つです。
- Singular tests
- Genetic tests
- パッケージからテストモジュールを読み込んでテストする
テストモジュールは多くあるため本記事では紹介しませんが、今後検証してご紹介したいと思います。
ここでは、Singular testsとGenetic testsの2つについてそれぞれ解説していきます。
Singular tests
Singular testsは、SELECT文を書いて簡単にテストしたいときに実行します。
テストには.sql
ファイルを使用し、dbt_project.yml
のtest_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を用いたデータ分析基盤の導入から内製化支援まで幅広く支援をしております。
もしご興味がある方は、お問い合わせ頂ければ幸いです。
また、dbt Labs, Inc. と販売パートナー契約を締結しており、dbtの販売と導入支援の提供が可能です。
そして、一緒に働いていただける仲間も募集中です!
APCにご興味がある方の連絡をお待ちしております。