2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

dbtのunit testを試してみた

Last updated at Posted at 2024-12-11

この記事は?

dbtのunit testを触ってみたメモです。

環境

  • Cloud IDE
  • version: versionless
    • unit test は1.7までは使えないため、versionless(1.8+)にしてください。(2024年12月9日現在)
  • DWHはsnowflake

unit testが動作するまでにやったこと

  1. ソースmodelの準備
  2. テスト対象modelの準備
  3. テスト用ymlの作成
  4. テストの実行
  5. エラー修正

ソースmodelの準備

テスト対象のmodelの元となるmodelの準備です。
社員番号と社員名が記載してある単純なmodelです。

my_test_raw.sql
select 
    emp_id,
    emp_name
from
    {{ ref("employee") }}

今回はテストを単純化するためにソース用のmodelを作成ました。
実際の環境ではソースmodelは作成しません(すでにあるmodelを使用)。

  • 社員番号(emp_id)
    • 0埋めの数字4桁
    • スプレッドシートで取り込んでいるため、桁落ちしているレコードがある
  • 社員名(emp_name)
    • 姓名は半角スペースで区切られている

テスト対象modelの準備

テスト対象modelの準備です。
ソースmodelを変換して、実際に使用するmodelを作成します。

my_test.sql
select
    iff(len(emp_id) < 4, lpad(emp_id, '0', 4), emp_id) as emp_id,
    trim(split(emp_name, ' ')[0], '"') as family_name,
    trim(split(emp_name, ' ')[1], '"') as first_name
from
    {{ ref("my_test_raw") }}
  • 社員番号(emp_id)
    • 4桁に満たない(0埋めが外れている)場合
      • 先頭に'0'を4文字になるまで埋める
    • 4桁以上の場合
      • emp_idをそのまま使用
  • 姓(family_name)
    • 社員名を半角スペースで分割して、前半を姓として使用
    • splitの仕様で「"」(ダブルクォート)が付加されるため、trimeで「"」を除去
  • 名(fisrt_name)
    • 社員名を半角スペースで分割して、後半を姓として使用
    • splitの仕様で「"」(ダブルクォート)が付加されるため、trimeで「"」を除去

テスト用ymlの作成

テスト用ymlを作成します。

my_test.yml
version: 2
unit_tests:
  - name: test_my_test
    model: 
      my_test
    # givenは入力する値を定義する
    given:
      # ここにはmodelのinputと同じことを書く
      - input: ref("my_test_raw")
        # model: my_test_rawの値
        rows:
          - {emp_id: '3', emp_name: '山田 奈緒子'}
          - {emp_id: '100', emp_name: '上田 次郎'}
          - {emp_id: '9789', emp_name: '矢部 謙三'}
    # expectは期待する値を定義する
    expect:
        # model: my_testで変換した値
      rows:
        - {emp_id: '0003', family_name: '山田', first_name: '奈緒子'}
        - {emp_id: '0100', family_name: '上田', first_name: '次郎'}
        - {emp_id: '9789', family_name: '矢部', first_name: '謙三'}

入力値の指定

givenセクションでは入力値を指定します。

  • input
    • ソースmodelのfromに該当する値を使用します
  • rows
    • テストデータを定義します
    given:
      # ここにはmodelのinputと同じことを書く
      - input: ref("my_test_raw")
        # model: my_test_rawの値
        rows:
          - {emp_id: '3', emp_name: '山田 奈緒子'}
          - {emp_id: '100', emp_name: '上田 次郎'}
          - {emp_id: '9789', emp_name: '矢部 謙三'}

期待値の指定

expectセクションではテスト結果の期待値を指定します。

  • rows
    • テスト対象のmodelで変換した後の値
    • 今回は以下の2点を変換しています
      • 社員番号→4桁0埋め
      • 社員名→姓・名の項目に分割
    # expectは期待する値を定義する
    expect:
        # model: my_testで変換した値
      rows:
        - {emp_id: '0003', family_name: '山田', first_name: '奈緒子'}
        - {emp_id: '0100', family_name: '上田', first_name: '次郎'}
        - {emp_id: '9789', family_name: '矢部', first_name: '謙三'}

テストの実行

準備ができたところでテストをやってみます!
Cloud IDEのコンソールから、以下のコマンドを実行をします。

dbt test --select my_test

image.png

エラーが出ました。
お・や・く・そ・く です。

エラーになった原因を探っていきましょう。

エラー修正

結果ログの「Details」をクリックすると、詳細なエラーメッセージが表示されます。
メッセージの最後の部分を見てみましょう。

image.png

actual differs from expected:

期待値と実際の値が異なる箇所は赤字で強調されています。

snowflakeの公式ドキュメントによると、LPADの仕様は…

LPAD(<base>, <length_expr> [, <pad>])

第1引数 第2引数 第3引数
文字列 埋める桁数 埋める文字

なので、SQLで書いたlpadの引数が誤っていたことになります。

    iff(len(emp_id) < 4, lpad(emp_id, '0', 4), emp_id) as emp_id,

次のように修正します。

    iff(len(emp_id) < 4, lpad(emp_id, 4, '0'), emp_id) as emp_id,

もう一度testコマンドを実行してみましょう。

image.png

無事テストが成功しました!

おまけ

JoinしたModelの場合

複数のModelをJoinして新たなModelを作成するケースはよく出てきます。

my_test_join.sql
select
    emp.emp_id,
    emp.emp_name,
    emp.org_code,
    org.org_name,
from 
    {{ ref("my_test_join_raw_emp") }} as emp
join {{ ref("my_test_join_raw_org") }} as org
    on emp.org_code = org.org_code

このケースでは、testのinputを複数指定します。

my_test_join.yml
version: 2
unit_tests:
  - name: test_my_test_join
    model: 
      my_test_join
    # givenは入力する値を定義する
    given:
      # ここにはmodelのinputと同じことを書く
      - input: ref("my_test_join_raw_emp")
        rows:
          - {emp_id: '0003', emp_name: '山田 奈緒子', org_code: '0001'}
      # 複数のテーブルをJoinしたModelはinputを複数羅列する
      - input: ref("my_test_join_raw_org")
        rows:
          - {org_code: '0001', org_name: 'マジシャン'}
    # expectは期待する値を定義する
    expect:
        # model: my_test_joinで変換した値
      rows:
          - {emp_id: '0003', emp_name: '山田 奈緒子', org_code: '0001', org_name: 'マジシャン'}

参考資料

この記事を作成するにあたり、以下の資料を参考にさせていただきました。
ありがとうございます。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?