Dataform CLI からテストコードを実行して、BigQuery UDF のユニットテストを実施してみる。

2024.06.25

こんにちは、みかみです。

犬×3と暮らしていると、なでられていない誰かがおこるかすねるかし始めるので、腕が3本欲しいです。

やりたいこと

  • BigQuery UDF のユニットテストを実施したい
  • GitHub `bigquery-utils` リポジトリで公開されてる、Dataform の BigQuery UDF ユニットテストを実行してみたい

前提

本エントリでは、Cloud Shell から Dataform CLI を実行しています。

また、Dataform や BigQuery などの操作に必要な API の有効化と権限付与は完了しています。

コミュニティ UDF のテストを実行

まずは、bigquery-utils リポジトリで公開されている、コミュニティ UDF のユニットテストコードを実行してみます。

README の通り、GitHub のリポジトリを clone して、テスト用ディレクトリに移動しました。

$ git clone https://github.com/GoogleCloudPlatform/bigquery-utils.git
$ cd bigquery-utils/dataform/examples/dataform_udf_unit_test

続いて、Dataform CLI をインストールします。

$ npm i -g @dataform/cli && dataform install
npm warn deprecated @aws-sdk/node-http-handler@3.374.0: This package has moved to @smithy/node-http-handler
npm warn deprecated vm2@3.9.19: The library contains critical security issues and should not be used for production! The maintenance of the project has been discontinued. Consider migrating your code to isolated-vm.

added 552 packages in 2m

92 packages are looking for funding
  run `npm fund` for details
Installing NPM dependencies...

Project dependencies successfully installed.

正常にインストールできたようなので、BigQuey に接続するための認証情報ファイルを作成します。

$ dataform init-creds bigquery

[1] US (default)
[2] EU
[3] other

Enter the location of your datasets [1, 2, 3]: 1

[1] ADC (default)
[2] JSON Key

Do you wish to use Application Default Credentials or JSON Key [1/2]: 1
Enter your billing project ID:
> ********

Running connection test...

Warehouse test query completed successfully.

Credentials file successfully written:
  /home/mikami_yuki/20240619/bigquery-utils/dataform/examples/dataform_udf_unit_test/.df-credentials.json
To change connection settings, edit this file directly.

※プロジェクトIDは伏字に変更しています。

これで準備完了です。 dataform test コマンドで、ユニットテストを実行してみます。

$ dataform test
Compiling...

Compiled successfully.

Running 189 unit tests...

int_b61f4634-ce97-49cc-96e9-dfac5aa6dc23: passed
int_1ff23f05-e8c3-4f0a-b58d-7fe4c6d261b6: passed
int_bd1845de-a64d-4076-9f50-d3ed0727a951: passed
(省略)
scaled_average_e49f2d39-b2f5-4729-b3fc-c99aa933e3e3: passed
scaled_sum_bf01f6f6-279f-4682-952b-dfed154a7151: passed
scaled_sum_b115d415-a948-455a-8d6b-a8348ad73b5a: passed

BigQuery コミュニティ UDF のユニットテストが、正常に実行できました。

自分のプロジェクトに作成した UDF をテストしてみる

自分のプロジェクトの BigQuery で以下の SQL を実行し、UDF を作成済みです。

CREATE OR REPLACE FUNCTION work.int(v ANY TYPE) AS (
  CAST(FLOOR(CAST(v AS FLOAT64)) AS INT64)
);

コードはコミュニティ UDF の int 関数を拝借しました。

以下のコマンドでメジャーバージョンを1に固定して、 Dataform CLI をインストールします。

$ npm install -g @dataform/cli@^1.2.0
$ dataform install

続いて README に記載の通り、環境変数で DATAFORM_DIR と自分の PROJECT_ID を設定して、Dataform プロジェクトを作成します。

$ DATAFORM_DIR=/home/mikami_yuki/20240621/udf_tests
$ PROJECT_ID=<your-bigquery-project-id>
$ dataform init bigquery $DATAFORM_DIR --default-database $PROJECT_ID
Writing project files...

Directories successfully created:
  /home/mikami_yuki/20240621/udf_tests/definitions
  /home/mikami_yuki/20240621/udf_tests/includes
Files successfully written:
  /home/mikami_yuki/20240621/udf_tests/dataform.json
  /home/mikami_yuki/20240621/udf_tests/package.json
  /home/mikami_yuki/20240621/udf_tests/.gitignore
NPM packages successfully installed.

※プロジェクトIDは適宜ご変更ください。

自分のGoogle Cloud プロジェクトに作成済みの UDF をテストするための、Dataform プロジェクトが作成できました。

$ ls -l
total 28
-rw-rw-r--  1 mikami_yuki mikami_yuki  188 Jun 21 15:49 dataform.json
drwxrwxr-x  2 mikami_yuki mikami_yuki 4096 Jun 21 15:49 definitions
drwxrwxr-x  2 mikami_yuki mikami_yuki 4096 Jun 21 15:49 includes
drwxrwxr-x 11 mikami_yuki mikami_yuki 4096 Jun 21 15:49 node_modules
-rw-rw-r--  1 mikami_yuki mikami_yuki   67 Jun 21 15:49 package.json
-rw-rw-r--  1 mikami_yuki mikami_yuki 7427 Jun 21 15:49 package-lock.json

先ほど clone した bigquery-utils リポジトリから、unit_test_utils.js を、コピーします。

$ cp /home/mikami_yuki/20240619/bigquery-utils/dataform/examples/dataform_udf_unit_test/includes/unit_test_utils.js includes/
$ ls -l includes/
total 8
-rw-rw-r-- 1 mikami_yuki mikami_yuki 5356 Jun 21 15:51 unit_test_utils.js

以下のテストコードを、definitions フォルダの下に、test_cases.js というファイル名で保存します。

const { generate_udf_test, generate_udaf_test } = unit_test_utils;

generate_udf_test("work.int", [
  {
    inputs: [`"-1"`],
    expected_output: `CAST(-1 AS INT64)`,
  },
]);

こちらのテストケースも、コミュニティ UDF int 関数のテストケースを拝借しました。

BigQuery 認証ファイルを作成します。

$ dataform init-creds bigquery

[1] US (default)
[2] EU
[3] other

Enter the location of your datasets [1, 2, 3]: 3
Enter the location's region name (e.g. 'asia-south1'):
> asia-northeast1

[1] ADC (default)
[2] JSON Key

Do you wish to use Application Default Credentials or JSON Key [1/2]: 1
Enter your billing project ID:
> ********

Running connection test...

Warehouse test query completed successfully.

Credentials file successfully written:
  /home/mikami_yuki/20240621/udf_tests/.df-credentials.json
To change connection settings, edit this file directly.

※プロジェクトIDは伏字に変更しています。

準備完了です。

ユニットテストを実行します。

$ dataform test
Compiling...

Compiled successfully.

Running 1 unit tests...

work.int_7bb87d52-3d83-4cfd-8a2e-99ec063cbfc9: passed

正常に実行できました。

つまづいたところ

自分のプロジェクトの UDF のテストを実行しようとしたところ、以下のエラーが発生しました。

$ dataform test
Compiling...

  Compilation errors:
  index.js: Error: Could not resolve "test_inputs"
    at t.Session.resolve (/home/mikami_yuki/20240619/for_udf_test/node_modules/@dataform/core/bundle.js:1:385319)
    at s.resolve (/home/mikami_yuki/20240619/for_udf_test/node_modules/@dataform/core/bundle.js:1:259330)
    at /home/mikami_yuki/20240619/for_udf_test/includes/unit_test_utils.js:67:29
    at s.apply (/home/mikami_yuki/20240619/for_udf_test/node_modules/@dataform/core/bundle.js:1:260138)
    at a.compile (/home/mikami_yuki/20240619/for_udf_test/node_modules/@dataform/core/bundle.js:1:257894)
    at /home/mikami_yuki/20240619/for_udf_test/node_modules/@dataform/core/bundle.js:1:389014
    at Array.forEach (<anonymous>)
    at t.Session.compileGraphChunk (/home/mikami_yuki/20240619/for_udf_test/node_modules/@dataform/core/bundle.js:1:388988)
    at t.Session.compile (/home/mikami_yuki/20240619/for_udf_test/node_modules/@dataform/core/bundle.js:1:387070)
    at t.main (/home/mikami_yuki/20240619/for_udf_test/node_modules/@dataform/core/bundle.js:1:380350)

なお、自分は Dataform CLI をインストールするのは初めてで、コミュニティ UDF のユニットテストを実行した後に Cloud Shell のセッションが切れてしまったので、再度 npm i -g @dataform/cli && dataform install コマンド実行してから README 記載の手順を実行していました。

リポジトリコード@dataform/core バージョン確認したところ 1.20.0 でした。 一方、自分で作成した Dataform プロジェクトの core バージョンおよび Dataform CLI バージョンは、 2.9.0 になっていました。 npm install -g @dataform/cli@^1.2.0 コマンドでバージョン指定追加したところ、エラー発生することなくテスト実行できました。

dataform test コマンド実行でエラー発生する場合は、以下の Dataform core と Dataform CLI のバージョンをご確認ください。

$ cat node_modules/@dataform/core/package.json
{
(省略)
    "version": "1.22.2",
    "name": "@dataform/core",
    "description": "Dataform core API.",
    "main": "bundle.js",
    "types": "bundle.d.ts"
}
$ npm list --depth=0 -g
/usr/local/nvm/versions/node/v20.14.0/lib
├── @dataform/cli@1.22.2
(省略)

まとめ(所感)

Dataform は使わずに、BigQuery 単体で UDF を実装している場合でも、Dataform CLI を使って簡単にユニットテストを実装&実行することができました。 また、Dataform は Google Cloud コンソールから簡単に使い始められるものの、Dataform CLI 実行環境を準備するのは少し敷居が高く感じていましたが、Cloud Shell を使えば簡単に環境構築できました。

料金に関しても、テスト時に BigQuery で SQL 実行するので、BigQuery のクエリスキャン料金は発生しますが、Dataform 自体の利用には課金は発生しません。

ユニットテストコードを実装しておけば、単体テストの実施工数も削減できますし、テストの自動化やリグレッションテストに活用することができます。 BigQuery の UDF をご利用の場合には、Dataform CLI からのユニットテストの導入も、合わせてご検討いただくのも良いのではないかと思いました。

参考