IBM iプログラム開発での単体テストの活用
コンポーネントのテストでアプリケーションの品質を向上
私はIBM iシステム向けのソフトウェアの変更管理用(SCM: Software Change Management)ソリューションを提供している会社の共同所有者として、ソフトウェアのテストの必要性について知りすぎているといってよいでしょう。約6年前にフロントエンドをJavaで開発するように切り替えた際に、Eclipseにおけるコードの品質管理の優雅さ、簡素さ、完全さに畏敬の念を感じました。Javaのコミュニティではコーディング・スタイルに曖昧さが全くないのです。プログラムをどう書くべきかについて確固たる合意が形成されているのです。コードの品質の分野で特に光る小さな宝石のひとつが単体テストの分野で、これはIBM iアプリケーションの開発にも適用が可能です。本稿では単体テストの速習版を提供し、iプログラム上で実行可能なテスト例をいくつかご説明します。
単体テストとは?
単体テストは、ソフトウェアのある部分をシステムの他の部分から可能な限り独立させてテストするという技法です。ある部分とは1つのプログラム、プロシージャ、コマンド、データ領域、その他外部から操作してその部分がどれくらい堅牢に作られているかを試すことができる単位のものです。テストできることの1つに、でたらめのパラメータ入力に対してプログラムがどのように反応するかを見るというものがあります。パラメータとして10進数を渡す必要がある場合に、テキストを渡したらそのプログラムはどのように反応するでしょうか。パラメータの受け取りを拒否してプログラムを停止すべきでしょうか、それともデフォルト値を使用して処理を続けログに記録すべきでしょうか、あるいはパラメータのチェックをせずに上手くいくようにと願うのでしょうか。通常の運用状況ではこのようなことは決して発生しないので、このようなテストの必要性を認めないと論じる方もいるかもしれません。確かにそうかもしれませんが、マーフィーの法則にもある通り、起こる可能性のあることはまさに最悪の状況の時に起こるのです。
顧客データベース用のAPIや入出力モジュールを作成し、それが正常に動作するかどうかを確認したいとしましょう。このAPIまたはモジュールは顧客データを作成し、顧客情報を取り出し、顧客の属性を更新し、そして顧客データを削除できなければなりません。このようなプログラムこそ単体テストの格好の候補なのです。運用環境の変更に備えるたびに用意した単体テストを実行すれば、システムの他の部分にどんな変更があったとしてもこのAPIは仕様通りに動作することを確認できます。これが保証されることで快適な気分になれます。
悲しいことに今日まで、私たちはSystem iの低レベルの単体テストをあまり実行していませんでした。私の会社であるリメイン・ソフトウェア社は、System i上での単体テストをどのように実行すれば良いのかというツィッター上での問いかけに応えるために、最近IUnitという単体テストプロジェクトを開始しました。その答えは、えー、良くわからない。テストだって? プログラムを実行してみてどうなるか見てみればよいのでは。
でもちょっと待ってください。Java/Eclipse/Junitで学んだ単体テストはどうなのでしょうか。あれはSystem iには適用できないのでしょうか。実は適用できるのです。そして、当社のウェブサイトからダウンロード可能なとても簡単なプログラムでそれについて説明します。
はじめに
単体テストはおそらく、ソフトウェアのテストの領域に入るもっとも簡単な方法です。それはなぜでしょう。それは単体テストが一度書けば何度も繰り返し実行できるソリューションで、この領域に入るレベルが低いからです。単体テストの結果と統計情報は簡単に管理職に提示することができます。単体テストは簡単で、素早く実行でき、しかもプログラムの障害(バグ)が減っていくという劇的な結果を見せてくれます。単体テストを試しに実行してみれば、そのパワーをすぐに実感できるとお約束します。
単体テストはプログラマ、システム管理者、管理職に対して具体的な恩恵をもたらすものです。もしあなたがこのうちのどれかに該当するのであれば、以下のような質問をご自分に投げかけてみてください。
プログラマ:
- 大きな変更をした後、自信がありますか。
- コマンド・パラメータのすべての組み合わせを本当にテストしましたか。
- この小規模な変更は本当に害を及ぼしませんか。
システム管理者:
- 新しいシステムに切り替える際、パラメータがすべて正しいと検証できますか。
- 権限を採用する新しいプログラムがありませんか。
- ユーザー・プロファイルはすべて依然として正しいですか。
- すべてのシステムに対して接続が確認できますか。
- サブシステムはすべて稼働していますか。
管理職:
- テストをしたいがどこから始めたら良いかわからないのですか。
- 結果を早く見たいですか。
- 実運用環境での障害件数を劇的に減らしたいですか。
コンポーネント・テスト
単体テスト、あるいはコンポーネント・テストの基本的考え方は、1つのコンポーネントを対象としてそのコンポーネント用の小さなテスト・プログラムを1つ書き、それが正しく実行されることを確認する、というものです。そして、プログラムを変更する度にそのテストを手動であるいはSCMシステム中に組み入れて再度実行します。これを繰り返すと数百回の小さなテストを実行したことになり、それが全体としてそのソフトウェアの品質を保証することになります。コンポーネント・テストを始めるには、バグ・レポートを見てそのバグが存在していることを証明するための小さなプログラムを書くことから始めると良いでしょう。次にそのバグが解消できたら、その同じ小さなテスト・プログラムを呼ぶことで確かにバグが解消できたことを証明できます。この小さなテスト・プログラムは永久的にあなたの手元に残って、そのバグが2度と出現しないことを保証します。
単体テスト、特に本稿で紹介しているIUnitテスト・フレームワークは対話型のプログラムを実行することを意味していない点に注意することが重要です。画面入力や発注数量などとともに受注プログラムの全フローをチェックするテストをする必要がある場合は、自動テスト・ソフトウェアへ投資することを検討してください。
しかし受注プログラムの内部を見てみると、小さな単位がたくさんあるのにお気づきになると思います。これらのたくさんの小さな単位が日付の取得、通貨の変換、受注レコードの記録、メッセージの取得などといった無数の小さなタスクを実行しているのです。これらの小さな単位こそが単体テストの対象となるプログラムなのです。
IUnitの動作
IUnit単体テスト・プログラムはいたって単純です。IUnitは5つのプログラム、3つのテスト例、1つのメッセージ・ファイルだけからなるテンプレート・ライブラリで構成されています。プログラムの一覧は以下の通りです。
- @RUNALLは渡されたライブラリ内にある@で始まらないすべてのプログラムを実行します。
- @RUNONEはプログラムを1つだけ実行し、パラメータとしてライブラリとプログラム名の2つを受け取ります。
- @LOGはジョブ・ログにメッセージが送られるたびに呼び出され、パラメータはありません。
- @SETUPは@RUNALLと@RUNONEによって、それが存在するときに限って1度だけ呼び出されます。@SETUPを使用して環境を設定します。
- @TEARDOWNは@RUNALLと@RUNONEによって、それが存在するときに限って1度だけ呼び出されます。@TEARDOWNを使用してテスト終了後に後片付けをします。
上記のプログラムに加え、実際にテスト・プログラムとなっている3つの特別なプログラムが提供されます。そのテスト・プログラムはFAIL、PASS、TESTXMPという名前です。このテスト・プログラムは@で始まっていないという点に注意してください。
ルール
IUnitを使用するには、以下の単純なルールに従わなければなりません。
- テスト・プログラムはスタンド・アローン単位で呼び出し可能なものとして扱わなければなりません。1つのテスト・プログラムが他のテスト・プログラムの出力に依存するようなことは可能な限り避けてください。各テストはそれ自体独立した形で実行できなければなりません。
- テストが失敗した場合、そのテスト・プログラムはエスケープ・メッセージを(CPFxxxx)で監視されている範囲内にある呼び出し側に送信します。QCPFMSGが送信するCPF9898をそれに使用できます。テストが成功した場合、そのテスト・プログラムはエスケープ・メッセージを送信しません。
- QSYSOPRメッセージ・キュー内にメッセージを残しておくとすべてのテストが停止してしまうので、QSYSOPRメッセージ・キュー内にメッセージを残しておくことは許されません。コミュニティ(そうですIUnitはオープン・ソースなのです)でヘルプを求めればこれは将来のリリースで、たとえばテストを別々のスレッドやジョブで実行するなどして、きっと解決されると思います。
ダウンロードとインストール
IUnitを使い始めるにはブラウザでwww.remainsoftware.com/iunitを開き、savefileをダウンロードして自分のSystem iにFTP転送します。System i上で以下のコマンドを実行します。
CRTSAVF QGPL/IUNIT
次に、Windowsのコマンド・ラインから以下のコマンドを実行します。
cd [.zipファイルを解凍した先のディレクトリ名]
ftp [System iの名前]
[ユーザー名/パスワード]
cd qgpl
bin
put iunit.savf iunit
quit
そしてSystem i上で次のコマンドを実行します。
RSTLIB SAVLIB(IUNIT) SAVF(QGPL/IUNIT)
上記のIUnitのダウンロード・サイトにはEclipse/Rationalのプラグインと使用方法説明書も用意されています。このプラグインを使用するとEclipseあるいはRationalのワークベンチからIUnitのテストを起動することができます。
テストの実行
前述の通り、IUnitのライブラリにはFAIL、PASS、TESTXMPという3つのテスト例がすでに含まれています。失敗するテストの例を見るには図―1に示すプログラムを見てください。成功するテストの例を以下に示します。
PGM
DLYJOB DLY(1)
ENDPGM
意味のあるテスト
さてこれまでいくつかの簡単な例を見てきましたので、意味のあるテストを見ていきましょう。SNDMSGコマンド用の意味のあるテストを図―2に示します。SNDMSGコマンドの目的は他のユーザーにメッセージを送信することです。もちろん、メッセージは無傷で届けられなければならず、それをこの小さなプログラムでテストしようというのです。
すべてのテストを実行する
すべてのテストを実行するにはまずIUNITライブラリを回復し、次に以下のように@RUNALLプログラムを呼び出します。
===> call iunit/@runall iunit
数秒後に以下のようなメッセージが出力されます。
===> call iunit/@runall iunit
00003 tests ran. 00002 failed, 00001 good.
ジョブ・ログには以下の情報が出力されています(ヒント: ジョブ・ログは@TEARDOWNの中で印刷することが可能です)。
4 > call iunit/@runall iunit @SETUP program called Boom. IUNIT/FAIL unit test failed. See previous message for details The text was not the same, see dump. IUNIT/TESTXMP unit test failed. See previous message for details End of file detected for file @RUNALL in QTEMP. @TEARDOWN program called 00003 tests ran. 00002 failed, 00001 good.
1つのテストを実行する
1つのテストを実行するには、IUNITライブラリから以下のように@RUNONEプログラムを実行します。
===> call iunit/@runone (iunit pass)
数秒後に以下のようなメッセージが出力されます。
00001 tests ran. 00000 failed, 00001 good.
IUnitを使用する際のコツ
IUnitを最大限に活用するためのコツをいくつかご紹介します。
- 各プロジェクトまたはドメインに対して単体テストのライブラリを1つ用意してください。IUnitのライブラリはそのままにしておくか、あるいは汎用なユーティリティを格納するのに使用してください。
- @LOG、@TEARDOWN、@SETUPを変更してテスト・ライブラリに保存する必要がある場合は、そのコピーを作成してください。リメイン・ソフトウェア社はこれを実行するためのコマンドを後日開発予定です。あるいはそのようなコマンドを作って送っていただけるとなお良いのですが。
- パラメータのないプログラムを作成してその中にテストを分離させてください。
- 大きいテストを数個作るのではなく小さなテストをたくさん作ってください。小さいテストの方が保守するのが容易です。
- 社内用にテストについて文書化してください。文書化しておけば特定のプログラムやコマンドがどのような意図で作られたのかがわかるので、他のプログラマにとって役に立ちます。
- テスト・ライブラリ中にユーティリティ・プログラムのようなものが必要となった場合は、そのプログラムの名前の前に@を付けておいて、IUnitに呼び出されないようにしておいてください。
- 創造力を働かせてください。
位置について、用意、テスト!
本稿で紹介した例は単体テストをどのように実行するのかを説明し、自分で単体テストをやってみようという方の手助けをするものです。単位とみなせるものはすべてIUnitユーティリティの助けを借りてテストすることができます。すべてのテストはIUnitから容易に呼び出すことができるようにプログラムになっていなければなりません。ただし、プログラムの内部からはモジュールからサービス・プログラムに至るまで何でもテストすることができます。
非常に簡単なエントリ・レベルから強力なツール群まで紹介しました。単体テストは企業の資産として世界中で認められています。単体テストはプログラムの品質を保証するだけでなく、特定の単位プログラムをどのように使うのかを新しいプログラマに教えるための例としての役割も果たします。ちょっと時間を作ってIUnitを使ってみてください。ご質問があればwim.jongman@remainsoftware.com宛てにあるいはツイッターで@wimjongmanまでご連絡ください。