スタブを使用したテストとSQL
スタブプログラムは、特段何の機能も持たないが本当のプログラムのプレースホルダ―としておかれるプログラムのことを言います。この本当のプログラムが実際に存在する場合もあれば、存在しないこともあります。私は、プログラムの変更をテストするため、長年にわたってこれを使用してきました。スタブサブルーチンやスタブサブプロシージャなどもあります。今回は、同様の概念をSQLクエリに応用する方法について説明したいと思います。
スタブの勉強には好みの検索エンジンを使用してかまいませんが、まず私が何の話をしているかを理解していただくため、例を挙げましょう。例えば、私はいまRPGプログラムを呼び出すCLプログラムを修正しようとしているとします。このCLプログラムには大量の条件論理が含まれていて慎重なテストを要しますが、RPGプログラムには修正を加える必要はありません。
. . . some code
call rpgpgm1 parm(&Cancel)
if cond(&Cancel *ne '1') do
call rpgpgm2 parm(&SomeDate &SomeNumber &Status)
select
when (&Status *eq 'A') do
. . . whatever
enddo
when (&Status *eq 'F') do
. . . more code
. . . etc.
. . . and so forth
ここでちょっとしたCLプログラムを書いて、それにPRGPGM1という名前を付けます。
pgm parm(&Status)
dcl &Status *char 1
chgvar &Status '0'
endpgm
本当のRPGPGM1が実際にRPGで書かれているかどうかは問題ではありません。また、本当のPRGプログラムに何が入力されるかも重要ではありません。この短いCLプログラムは、私が検証をするために必要なことをしてくれるのです。
もちろん、RPGPGM1の呼び出しをコメントアウトして、一時的にCHGVARコマンドを追加し、&CANCELの値をゼロにすることもできますが、そうなると本番で使用されるCLコードと違うものになってしまうでしょう。
私が使う中でも一番短いスタブプログラムはこちらです。非常によく使います。
pgm
endpgm
私はこのソースメンバーを、テストライブラリにコンパイルし、プログラムのオブジェクトが持っていなければならない名前を付けます。
スタブプログラムは、フローとロジックを検証する際には非常に便利です。次にSQLについて考えてみましょう。同じようなことをできるでしょうか。つまり、検証とは全く関係のないデータにアクセスすることなく、クエリの一部分のみを検証することはできるでしょうか。答えは、イエスです。
多くのショップでは、テストは本番用データ、あるいは本番用データのコピーを使って行われます。これが良いやり方かそうでないかは重要ではありません。実際に行われているというだけです。そのようなデータを使用して行われるテストは多くの場合、信頼度が低いか、非常に難しいものです。これには主に2つの理由があります。
- データが多すぎる(結果セットの行が多すぎる)
- データがすべての論理条件を実行するために必要な値をすべて含んでいない
これらの阻害要因は、必要なテーブルとビュー(物理ファイルと論理ファイル)をテストライブラリに作成し、必要なデータのみのテーブルをロードすることで解決できます。それも良いアプローチではありますが、時間がかかります。例えば、ビューの一つがテーブルのビューのビューだったらどうでしょうか。セットアップするだけでもかなりの時間がかかってしまいます。
同じ目的を達成するための別の方法があります。こちらの方がずっと簡単です。
以下に例をお見せします。顧客情報を取得する非常に洗練されたクエリです。
select c.CusNum, c.LstNam,
c.City, c.State,
c.ZIPCod
from qcustcdt as c
order by c.CusNum;
192837 | Lee | Hector | NY | 14841 |
389572 | Stevens | Denver | CO | 80226 |
392859 | Vine | Broton | VT | 5046 |
397267 | Tyron | Hector | NY | 14841 |
475938 | Doe | Sutter | CA | 95685 |
583990 | Abraham | Isle | MN | 56342 |
593029 | Williams | Dallas | TX | 75218 |
693829 | Thomas | Casper | WY | 82609 |
839283 | Jones | Clay | NY | 13041 |
846283 | Alison | Isle | MN | 56342 |
938472 | Henning | Dallas | TX | 75217 |
938485 | Johnson | Helen | GA | 30545 |
例えば、このクエリによる出力データを利用する人が、2文字の略称ではなく州の名称を出力したいと考えたとしましょう。そこで「STATES(州)」というテーブルを追加しました。
select c.CusNum, c.LstNam,
c.City, coalesce(s.Name,c.State) as State,
c.ZIPCod
from qcustcdt as c
left join states as s
on c.State = s.Abbreviation
order by c.CusNum;
変更を確認するため、クエリを実行してみます。すると、このようになりました。
192837 | Lee | Hector | New York | 14841 |
389572 | Stevens | Denver | Colorado | 80226 |
392859 | Vine | Broton | Vermont | 5046 |
397267 | Tyron | Hector | New York | 14841 |
475938 | Doe | Sutter | California | 95685 |
583990 | Abraham | Isle | Minnesota | 56342 |
593029 | Williams | Dallas | Texas | 75218 |
693829 | Thomas | Casper | Wyoming | 82609 |
839283 | Jones | Clay | New York | 13041 |
846283 | Alison | Isle | Minnesota | 56342 |
938472 | Henning | Dallas | Texas | 75217 |
938485 | Johnson | Helen | Georgia | 30545 |
一見うまくいったように見えますが、QCUSTCDTの州名はSTATESテーブルにあるので、COALESCE関数が想定通りに機能したかは確認することができません。これを検証する方法は2つ考えられます。
- QCUSTCDTの検証用コピーに無効な州コードを含んだ行を追加する
- STATESテーブルから1つ以上の州を削除する
しかし、もっと簡単な方法があります。それは、スタブを使ってSTATESテーブルに代入する方法です。次のようにします。
select c.CusNum, c.LstNam,
c.City, coalesce(s.Name,c.State) as State,
c.ZIPCod
from qcustcdt as c
-- left join states as s
left join (values ('TX', 'Texas')) as s (Abbreviation, Name)
on c.State = s.Abbreviation
order by c.CusNum;
STATESテーブルへの参照をコメントアウトし、代わりに1行の代入データを行値式として定義しました。
-- left join states as s
left join (values ('TX', 'Texas')) as s (Abbreviation, Name)
この行値式には、同じカラム(フィールド)名が与えられています。ABBREVIATION とNAMEです。STATESテーブルには他のカラム(フィールド)があるということは問題ではありません。ここでは、この行値式に同じ相関名である「S」一文字を割り当てました。
この代入テーブルでは1行だけしか定義していないので、テキサスの場合には州名が入り、その他の州の場合には州コードが入るはずです。ではやってみましょう。
192837 | Lee | Hector | NY | 14841 |
389572 | Stevens | Denver | CO | 80226 |
392859 | Vine | Broton | VT | 5046 |
397267 | Tyron | Hector | NY | 14841 |
475938 | Doe | Sutter | CA | 95685 |
583990 | Abraham | Isle | MN | 56342 |
593029 | Williams | Dallas | Texas | 75218 |
693829 | Thomas | Casper | WY | 82609 |
839283 | Jones | Clay | NY | 13041 |
846283 | Alison | Isle | MN | 56342 |
938472 | Henning | Dallas | Texas | 75217 |
938485 | Johnson | Helen | GA | 30545 |
これで、COALESCE関数が思った通りの動作をしてくれたことを確認できました。スタブを外してSTATESテーブルを接続しても大丈夫です。
行値式を使用することによって、本番で実行されるコードと違うものを実行しているのではないか、と反論することもできるでしょう。私はそれを否定はしません。ただ、高いレベルの抽象化を行うことで、データがどこから得られたものかということを考えずに、どのような形で出力されるかにのみ集中できるということなのです。
今回、スタブには1行だけしか必要なかったので1行だけ使用しましたが、必要に応じて2行以上にすることもできます。例えば、以下の通りです。
left join (values ('TX', 'Texas'),
('CO', 'Colorado')) as s (Abbreviation, Name)
行値式を追加して、それぞれをカンマで区切っていくだけです。
時には本番環境に十分な検証をしていないソフトをインストールしてしまう人もいるかと思います。それは、もしかすると、十分なテストを設定するのがあまりにも難しいという理由からかもしれません。そんな時は今回紹介したツールが役に立つことでしょう。