dbt-coreのunittestでsql_headerが使えない問題のmonkeypatch
現時点(2024年9月)では、dbt-coreのunittestではsql_headerが使えないので、
その課題に対するmonkeypatchを当ててみました。
[Feature] Make sql_header configuration available on tests | dbt-labs/dbt-core | GitHub
https://github.com/dbt-labs/dbt-core/issues/9775
特にBigQueryを使っている場合は、
sql_headerに一時UDFを定義することになるので、
テストできるモデルが制限されてしまいます。
Create a BigQuery Temporary UDF | sql_header | docs.getdbt.com
https://docs.getdbt.com/reference/resource-configs/sql_header#create-a-bigquery-temporary-udf
sql_headerに一時UDFを定義するという仕様自体がworkaroundっぽい気もするので、
とりあえずmonkeypatchを当てて対応してみることにしました。
パッチ一覧
作成したパッチの一覧です。
patch for bigquery unittest with sql_header | takemikami/dbt-core | GitHub
https://github.com/takemikami/dbt-core/commit/d329c152f036b9249422f77acd0432091d2c88ac
patch for bigquery unittest with sql_header | takemikami/dbt-adapters | GitHub
https://github.com/takemikami/dbt-adapters/commit/780acadfcf363c0928b742ba1db6bded2bc89325
ざっくり言うと、
dbt-core側で、jinjaのcontextにsql_headerの内容を渡し、
dbt-adapters側の、jinjaテンプレートでsql_headerをrenderしています。
使い方
以前のエントリで試したBigQueryEmulatorを使う前提で、使い方の流れを示します。
BigQueryEmulatorを使ってdbt-coreを動かす検討(monkeypatchを当てて) | takemikami.com
https://takemikami.com/2024/0916-sqlfluff-dbt-bq-emulator.html
dbtのプロジェクトを作る
dbtのプロジェクトを作ります。
Python仮想環境を用意して、パッチを適用したdbt-coreをインストールします。
$ mkdir dbt-bqpatch-study && cd $_
$ python -m venv venv
$ . venv/bin/activate
$ pip install git+https://github.com/takemikami/dbt-core@d329c152f036b9249422f77acd0432091d2c88ac#subdirectory=core git+https://github.com/takemikami/dbt-adapters@780acadfcf363c0928b742ba1db6bded2bc89325 git+https://github.com/takemikami/dbt-bigquery.git@4c84de2e72e876b3c4ed0307c36cfb5a97ac4e75
dbtを初期化します。
※project, datasetはemulator起動時のオプションと揃える。
$ dbt init study
12:08:14 Running with dbt=1.9.0-a1
12:08:14
Your new dbt project "study" was created!
For more information on how to configure the profiles.yml file,
please consult the dbt documentation here:
https://docs.getdbt.com/docs/configure-your-profile
One more thing:
Need help? Don't hesitate to reach out to us via GitHub issues or on Slack:
https://community.getdbt.com/
Happy modeling!
12:08:14 Setting up your profile.
Which database would you like to use?
[1] bigquery
(Don't see the one you want? https://docs.getdbt.com/docs/available-adapters)
Enter a number: 1
[1] oauth
[2] service_account
Desired authentication method option (enter a number): 1
project (GCP project id): test
dataset (the name of your dbt dataset): dataset1
threads (1 or more): 1
job_execution_timeout_seconds [300]:
[1] US
[2] EU
Desired location option (enter a number): 1
BigQuery Emulatorへの接続を設定する
~/.dbt/profiles.yml
を、次のように編集します。
※methodの変更、api_endpointを追加
study:
outputs:
dev:
dataset: dataset1
job_execution_timeout_seconds: 300
job_retries: 1
location: US
method: emulator
priority: interactive
project: test
threads: 1
type: bigquery
api_endpoint: http://127.0.0.1:9050
target: dev
dbt debug
で接続OKとなることを確認します。
$ cd study
$ dbt debug
一時UDFを追加してunittestを実行する
サンプルのモデルを、一時UDFを使ったコードに変更します。
models/example/my_first_dbt_model.sql
/*
Welcome to your first dbt model!
Did you know that you can also configure models directly within SQL files?
This will override configurations stated in dbt_project.yml
Try changing "table" to "view" below
*/
{{ config(materialized='table') }}
{% call set_sql_header(config) %}
create temp function add(x1 int64, x2 int64)
returns int64
as (
x1 + x2
);
{% endcall %}
with source_data as (
select add(1, 0) as id
union all
select add(1, 1) as id
)
select *
from source_data
/*
Uncomment the line below to remove records with null `id` values
*/
-- where id is not null
unittestを追加します。
study/models/example/unittest.yml
unit_tests:
- name: my_unit_test
model: my_first_dbt_model
given: []
expect:
rows:
- {id: 1}
- {id: 2}
unittestを実行して、成功することを確認します。
$ dbt test --select unit_test:study.my_unit_test
12:31:06 Running with dbt=1.9.0-a1
12:31:07 Registered adapter: bigquery=1.8.2
12:31:07 Found 2 models, 4 data tests, 481 macros, 1 unit test
12:31:07
12:31:07 Concurrency: 1 threads (target='dev')
12:31:07
12:31:07 1 of 1 START unit_test my_first_dbt_model::my_unit_test ........................ [RUN]
12:31:07 1 of 1 PASS my_first_dbt_model::my_unit_test ................................... [PASS in 0.36s]
12:31:07
12:31:07 Finished running 1 unit test in 0 hours 0 minutes and 0.39 seconds (0.39s).
12:31:07
12:31:07 Completed successfully
12:31:07
12:31:07 Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1
以上。