2018.02.08
Paul Tuohy 著

単体テストで役に立つ関数

RPG単体テスト プログラムにおいてリストを処理するために私が使用している手法を紹介したいと思います。ウィキペディアによると、「. . . 単体テストあるいはユニットテストとは、ソースコードの個々のユニット、すなわち、1つ以上のコンピュータプログラムモジュールが使用に適しているかどうかを決定するために、関連する制御データ、使用手順、操作手順とともにテストする手法である」とのことです。

モダンRPGの世界の言葉に言い換えると、コードの一定部分をテストするための「テスト」プログラムを作成すること、となるかと思われます。たとえば、あるサブプロシージャーを作成するときに、関連するテスト データを渡してそのサブプロシージャーを呼び出すテスト プログラムを作成するということです。こうしたテスト プログラムでは、入力値をプロンプトするために、およびサブプロシージャーの呼び出しからの応答および/または返されたパラメーターを表示するためにDSPLY命令が多用されるのが一般的です(「The Nearly Forgotten DSPLY Operation」を参照)。

DSPLY命令の使用は、単一の値を表示するのには申し分ありません。しかし、パラメーターの1つがリストだったとしたらどうでしょうか。そのようなケースでDSPLYを使用してリスト内の要素をループするとなると、非常に煩雑になります。DSPLY命令が表示できるのは最大で52文字だけであり、それぞれの値が表示されるたびにEnterを押す必要があります。 これに代る方法は、1度に1行ずつ出力するprintf() C関数を使用することです。

ここで紹介する例は、以下の6つのソース メンバーから作成されます。

メンバー 説明
APIC001 モジュールには、API_listObjects()サブプロシージャー(テスト対象となるサブプロシージャー)が入っています。
APIC001_P APIC001のためのプロトタイプ、テンプレート、および名前付き定数が入っているコピー メンバー。
ADPC001U API_listObjects()のための単体テスト プログラム。
STDAPIINFO システムAPIのためのプロトタイプ、テンプレート、および名前付き定数が入っているコピー メンバー。
UNIT_P unit_printLineサブプロシージャーのプロトタイプ。
UNIT01 unit_printLineサブプロシージャー。

printf()は多くのテスト ユニット プログラムで使用されることになるため、サービス プログラムでサブプロシージャーとしてラッピングしておくとよいと思われます。ひょっとしたら、サービス プログラムに追加したいと思うような単体テスト サブプロシージャーが他にもあるかもしれません。

unit_printLine()サブプロシージャー

以下はUNIT_Pソース メンバーです。

技術情報code01

unit_printLine()サブプロシージャーのプロトタイプの定義が入っています。パラメーター定義(text)はポインターを指定し、これは値で渡され、文字列へ変換されます。つまり、パラメーターとして任意の文字データを渡すことができ、それにnull終止符(16進数「00」)が付加されて、文字列となるということです。

UNIT01ソース メンバーには、unit_printLine()サブプロシージャーの定義が入っています。詳細については、コールアウトを参照してください。

技術情報code02

(A) CTL-OPTでは、QC2LEバインディング ディレクトリーのためのバインディング ディレクトリー キーワードが指定されています。これによってprintf()関数の使用が可能になります。

(B) printf()のプロトタイプを宣言します。

(C) 表示されるすべてのテキスト行の最後に、New Line文字が必要です。

(D) printf()関数を呼び出します。(‘%s’ + NEWLINE)というパラメーター値は、1つ目のnull文字までの文字が出力され、New Lineが続くことを示しています。

UNIT01はモジュールへコンパイルされ、そのモジュールはサービス プログラムUNITTESTに含められます。サービス プログラムUNITTESTのためのエントリーは、バインディング ディレクトリーUNITTESTに含まれます。 つまり、unit_printLine()サブプロシージャーを使用する必要があるプログラムは、プログラムのCTL-OPTでbndDir(‘UNITTEST’)エントリーを指定する必要があるということになります。

これでリスト プロシージャーの準備ができたので、その使い方の例を見てみましょう。

テスト対象となるサンプル サブプロシージャー

APIC001_Pソース メンバー内にあるサブプロシージャーAPI_listObjects()は、要求されたライブラリーのオブジェクトのリストを返します。

技術情報code03

APIC001_Pソース メンバーには、b_objectListデータ構造テンプレートの定義およびAPI_listObjects()サブプロシージャーのプロトタイプが入っています。b_objectListテンプレートは返されるリストのフォーマットを提供し、そのプロトタイプは、サブプロシージャーが数値(結果内の行数)を返し、ライブラリー名のin-outパラメーターがあり、オブジェクト記述およびエラー テキストの配列を返すことを示しています。

ダウンロード可能なコードの中に、ソース メンバーAPIC001およびSTDAPIINFOが入っています。これらのサブプロシージャーは、単体テストの作成とは直接的に関係がありません。私は完璧を期すためにそれらを含めました。問題があることを単体テストが示す場合以外は、このコードについては気にする必要はありません。

以下は、ソース メンバーAPIC001です。

技術情報code04

以下はSTDAPIINFOコピー メンバーであり、APIC001で参照されます。こちらも、単体テストの作成とは直接的に関係はなく、完璧を期すためだけに含めています。

技術情報code05

APIC001はCRTRPGMODコマンドを使用してコンパイルされます。

単体テスト モジュール

以下は、単体テスト モジュールAPIC001Uのソース コードです。詳細については、コールアウトを参照してください。

技術情報code06

(A) CTL-OPTには、UNITTESTバインディング ディレクトリーのためのバインディング ディレクトリー キーワードが指定されています。これによってunit_printLine()関数の使用が可能になります。

(B) テスト対象となるサブプロシージャー(API_listObjects())のプロトタイプをインクルードします。

(C) 単体テスト ルーチン(unit_printLine())のプロトタイプをインクルードします。

(D) テストするのに必要とされる任意の変数を宣言します。たとえばテスト対象となるサブプロシージャーの呼び出しに必要となるパラメーターなどです。

(E) ライブラリー名をプロンプトします。

(F) テスト対象となるサブプロシージャー(API_listObjects())を呼び出します。

(G) エラー テキストにデータが入っている場合は、それを表示します。

(H) そうでない場合、返されたリストをループし、unit_printLine()を使用してリスト内の各項目の内容を出力/表示します。

APIC001UはCRTRPGMODコマンドを使用してコンパイルされます。

APIC001Uプログラムを作成する

これで、2つのモジュールが準備できました。APIC001Uには単体テストが入っており、APIC001にはテスト対象となるサブプロシージャーが入っています。次のコマンドを使用して単体テスト プログラムを作成します。

技術情報code07

APIC001Uは、モジュールのリストの内の最初のモジュールでなければなりません。また、ACTGRP(*NEW)を指定する必要があります。これは活動化グループが終了するまでリストが表示されないからです。

APIC001Uプログラムを使用する

APIC001Uプログラムを呼び出し、存在しないライブラリーの名前が入力された場合は、エラー テキストの内容が表示されます。

技術情報code08

APIC001Uプログラムを呼び出し、存在しているライブラリーの名前が入力された場合は、返されたリストの内容が表示されます。リストはページ アップおよびページ ダウンすることができ、終了するにはEnterを押します。

技術情報code09

単体テストについてのさらに詳しい情報

ここで紹介したのは、リストを表示するシンプルな単体テスト プログラムの例です。ライブラリー名をプロンプトする代わりに、ライブラリーのリストを用意して、各ライブラリーのテストをループする、より複雑な単体テスト プログラムを作成することも可能です。

単体テストを自動化することに興味をお持ちなら、RPGUnitオープンソース プロジェクト(https://github.com/takshil/RPGUnit)をご覧になるとよいでしょう。

ページトップ

ボタン