はじめに
はじめまして。基盤開発チーム/Unit3を兼務している林です。
エムスリーではデータ基盤として BigQuery を全社的に使っていますが、 BigQuery を使ったアプリケーションやバッチを実装するときに、テストをどうするべきかは難しい問題です。*1
本記事では、BigQuery で記述されたロジックのテストの難しさを解決できるひとつの手法として、簡単に導入できるスタブデータを使うクエリの書き方を紹介します。*2
詳細は後述しますが、以下のようなメリットがある手法です。
- BigQuery 以外のツールを使わず、実際の BigQuery での文法や挙動を確かめられる
- 何もリソースを作らず、テストのためのスタブデータを簡単に用意できる
- 実際のテーブル名・列名・列の型と合っているかチェックできる
- はじめに
- テクニック1: WITH句で実際のテーブルを差し替え(シャドーイング)できる
- テクニック2: UNION するとき、1つ目の結果の列名と型が後続でも使われる
- スタブデータを使ったテストクエリは書けるものの
- テクニック3: 0行のSELECTでも列名と型を取り出せる
- 型安全な BigQuery スタブクエリの最終形
- We are hiring
実現に必要なテクニックを紹介していきつつ、最後に「型安全なスタブデータで BigQuery ロジックをテストする」簡単なやり方を紹介します。
テクニック1: WITH句で実際のテーブルを差し替え(シャドーイング)できる
BigQuery の公開サンプルデータの1つの bigquery-public-data.samples.shakespeare
を使って解説します。
まず、テーブルの存在を確認します。
word
列に、なにかしら単語が入っていることがわかります。
SELECT word FROM `bigquery-public-data.samples.shakespeare` LIMIT 1
このテーブルをWITH句で差し替えてみましょう。(※テーブルのパスをバッククオート `
で囲う必要があります)
WITH `bigquery-public-data.samples.shakespeare` AS ( SELECT 'm3' word ) SELECT * FROM `bigquery-public-data.samples.shakespeare`
| word |
---
| m3 |
なんと、実際にあるテーブルを隠して、こちらが指定したデータを出すことができてしまいました。 このテクニックだけでも、スタブデータをテストで使うことができます。 たとえば、BigQuery クライアントをラップして、任意のWITH句を挿入できるテスト用機能をつければよいだけです。
テクニック2: UNION するとき、1つ目の結果の列名と型が後続でも使われる
BigQuery で複数行のデータを SQL の記述で用意するときは、1行の SELECT を UNION でつなぐのが基本的なやり方です。
SELECT 'm3' word UNION ALL SELECT 'tech' word UNION ALL SELECT 'blog' word
列数が少なければ問題ないですが、これがたとえば10列あると、記述するのも視認するのも大変になります。
実は、2行目以降は1行目の列名と型名を引き継いでいるだけなので、以下のように書けます。
SELECT 'm3' word UNION ALL SELECT 'tech' -- 列名不要 UNION ALL SELECT 'blog' -- 列名不要
このように、2行目以降の列名や型の記述を省略できることを知っていると、列数が多いスタブデータについても、見やすく記述できるようになります。
なお、列名だけでなく列の型も引き継いでいるので、以下はエラーになります。
SELECT 'm3' word UNION ALL SELECT 3 -- 1行目はSTRING型なので型が合わずエラーになる
スタブデータを使ったテストクエリは書けるものの
テクニック1と2を先に知っていたので、BigQuery ロジックのテストに使っていましたが、1つ問題がありました。
実際のテーブルを差し替えたときに、実際のテーブル名や列名、列の型などを見ていないので、以下の状況では、間違ったテストを通してしまいます。
- スタブ時に列名などを間違える
- テスト対象のクエリでも同様に間違える
このために、テーブルをスタブしたテストの他に、実テーブルとの結合テストも一度は通しておかないと、テストとしては不十分でした。
完璧ではないものの、ここまでの内容でも実用的だと思っていたので、同僚とお寿司を食べに行ったときにこの内容を披露していたところ、即座に「実際のテーブルと何らかの方法で結合すれば型チェックも一発でできるかも」とアイディアをもらい、次のテクニックを発見したので、テックブログとして書きたくなりました。
テクニック3: 0行のSELECTでも列名と型を取り出せる
実際のテーブルからデータを取り出せば UNION でつないだときにそのスキーマ(列名・型)を使えますが、テストでは実データを見たくないので、「0行取り出したときにもスキーマ情報だけ使えればうれしい」ですよね。 その単純な発想でやってみたらできました。 実際のテーブルから0行抽出してみましょう。
SELECT word FROM `bigquery-public-data.samples.shakespeare` LIMIT 0
BigQuery で実行すると結果は表示されませんが、以下のように UNION でつないでみましょう。
(SELECT word FROM `bigquery-public-data.samples.shakespeare` LIMIT 0) UNION ALL SELECT 'm3'
| word |
---
| m3 |
0行抽出のときの列名を引き継いでいます。 また、以下のように型が合わないとエラーになります。
(SELECT word FROM `bigquery-public-data.samples.shakespeare` LIMIT 0) UNION ALL SELECT 3 -- 型エラーになる: Column 1 in UNION ALL has incompatible types: STRING, INT64
型安全な BigQuery スタブクエリの最終形
これまで紹介したテクニックを組み合わせた、型安全なスタブデータを使えるクエリの最終形です。
WITH `bigquery-public-data.samples.shakespeare` AS ( (SELECT word FROM `bigquery-public-data.samples.shakespeare` LIMIT 0) -- ここでは実際のテーブル UNION ALL SELECT 'm3' UNION ALL SELECT 'tech' UNION ALL SELECT 'blog' ) -- テストしたいクエリ(テーブル名は本物に見えるがWITH句でスタブされている) SELECT SUM(LENGTH(word)) AS n FROM `bigquery-public-data.samples.shakespeare`
| n |
---
| 10 |
テスト対象のクエリでテーブル名や列名を間違えたらエラーになるし、STRING型のところにINTEGERのスタブデータを入れたらエラーになるし、実際のデータは何も使わないし、任意のデータを差し込んでクエリ計算することができる。
いかがでしょうか。 テストが書けない書きづらいと思っていた BigQuery ロジックに、サクッとテストを導入できる可能性を感じていただけたのであれば幸いです。
なお、本記事では簡単なクエリに対しての例を紹介しましたが、実際のテスト対象のクエリが WITH や WITH RECURSIVE で始まっていたりする場合にできるかどうかを考えてみるのも面白いと思います。
We are hiring
余談ですが、エムスリーではすでにコーディングにAIを全面活用していますが、前提知識を与えないで本記事のアイディアがいきなりAIから出てくるかというと、そうではない気がしています。 適切な問いを立てればAIが解けるものも、そもそも問題意識を持って考え続けるのは、まだ人間の方ができることがあるかもしれません。
同僚と楽しくお寿司を食べているときもテストや型安全を考えて改善していく、こんなエムスリーに興味を持たれた方は、是非カジュアル面談などご応募ください!
*1:DBを使ったテストが難しいのは BigQuery に限らない部分もありますが、PostgreSQL や MySQL、DynamoDB などと違って、ローカルで公式実装のテスト用コンテナを建てられないという困難がある点については同意していただけると思います。
*2:BigQuery の使い方及びテストについて、色々なやり方が考えられます。弊社でも、BigQuery 基盤を整備している側のデータ基盤チームでは dbt が使われていたり、BigQuery データを最も使い倒している AIチームでは Go製のOSSである bigquery-emulatror GitHub - goccy/bigquery-emulator: BigQuery emulator server implemented in Go などが使われています。本記事では、BigQuery に対して SQL を投げて結果を取得する、それだけの機能を前提としたアドホックな新しいテスト手法の提案を主眼においています。