PostgreSQLにいくつかパッチを投げている須藤です。パッチのレビューの中で、PostgreSQLのリグレッションテストツールpg_regress
に今まで知らなかった機能があったことを知ったので紹介します。
pg_regress
pg_regress
はいわゆるエンドツーエンドのテストツールです。PostgreSQLクライアントでサーバーに接続してSQLを実行し、そのときの実行結果があらかじめ用意してある期待値と同じになっているかどうかでテストします。MySQLでいえばmysqltest
、Groongaでいえばgrntest相当のツールです。
Webアプリケーションのテストではエンドツーエンドのテストは実行時間が長くなりがちでできるだけ書きたくないテストになりやすいですが、データベースのテストの場合はそうでもありません。たしかに、Cで書いた単体テストのほうが実行時間が短くなりやすいですが、CではなくSQLでテストを書けるので、エンドツーエンドのテストの方がメンテナンスしやすいです。また、実際にユーザーが使うときと同じ一連の処理を少ないテストでカバーできることもメンテナンスのしやすさにつながります。Groonga・Mroongaは、昔は単体テストも書いていましたが、今はエンドツーエンドのテストしか書いていません。
ということで、PostgreSQLのテストを書くにあたりすごく便利なツールがpg_regress
です。
pg_regress
が苦手なところ
便利といってもなにもかもpg_regress
でカバーできるわけではありません。pg_regress
はSQLでテストを書くので、SQLで書かないようなことは苦手です。
たとえば、SQLを発行する前の接続関連のテストは苦手ですし、レプリケーションのように複数の接続にまたがるようなケースや、PostgreSQL起動時に設定をしなければいけないようなケースも苦手です。
環境によって結果が変わるようなケースも苦手だと私は思っていました。
pg_regress
はPostgreSQLクライアントとしてpsql
を使うので、SQLではできないけどpsql
でできることならできます。たとえば、条件分岐をできます。SQLでもplpgsql
を使えば条件分岐をできますが、psql
の方がもっとがんばれる気がします。(気のせいかもしれません。)
そんな便利機能を使えば、環境によって結果が変わるようなケースでも対応できることがあります。たとえば、PGroongaではPostgreSQLのバージョンによって結果が変わるケースを\g |sed
で結果を正規化して環境が変わっても結果が同じになるようにして対応していました。デバッグしやすいHTMLのテストの書き方の「assert_match
問題」と同じアプローチです。
しかし、古いPostgreSQLでは機能がなくてエラーになるケースもあり、そのような場合は結果を正規化することはできません。そのようなケースにどう対応していたかというと、該当のテストファイルを削除してテストを実行しないようにしていました。
でも、そんなことをする必要はなかったのです!
期待値ファイル
pg_regress
はsql/test.sql
を実行し、その結果を、事前に用意しておいた期待した結果が入ったexpected/test.out
の内容と比較します。同じであれば成功、そうでなければ失敗です。私はこの期待値ファイルを各テストごとに1つだけ用意しなければいけないと思っていました。
が、実は、複数の期待値ファイルを用意して、そのどれかと同じ内容であれば成功、というようにできたのです。
この例の場合ではexpected/test.out
だけでなく、expected/test_1.out
のように_[0-9]
を追加したファイルも用意しておくことができたのです。0から9まで使えて、追加で10個用意できますが、PostgreSQL本体のテストでは_1
か_2
しか使っていませんでした。
ということで、通常の期待値はexpected/test.out
として用意し、特別な環境用の期待値ファイルをexpected/test_1.out
として用意することでどの環境でもいい感じにテストを実行することができます。
grntestではまた違った形で環境に依存するテストをいい感じに扱えるようにしていますが、こういうアプローチもあるなぁと思ったのでした。
まとめ
みなさんは、レビューされるときはどんな気持ちでレビューされているでしょうか。レビュアーからの指摘をそのまま反映することでレビューを通すことを目指す!というのも一つのやり方だとは思いますが、レビューから学ぶという気持ちでのぞんでみてもいいかもしれません。
ちなみに、私がこのpg_regress
の機能を知ったレビューコメントはRe: confusing / inefficient "need_transcoding" handling in copyでした。