DelphiとFreePascalで動作する自作ライブラリのテストコードを書くために、自作のxUnitフレームワークを使っています。
自作のテストフレームワークでは、 Test
で始まるメソッド名をテストメソッドとして自動で実行するxUnitフレームワークでよくある仕様としています。
このような仕様を実装する場合に、フレームワーク側でテストコード側のメソッド名を取得するコードが必要となります。 私のテストフレームワークでは、リフレクションでこれを実装することにしました。
元々Delphiのみ対応で最初に作成したのですが、FreePascalに対応するPullRequestをもらってマージしたので、そこからはDelphi, FPC両対応としてメンテナンスしています。
DelphiとFreePascalでリフレクションの実装状況に違いがあるので、コンパイラ指令で環境ごとに処理を分けることになります。
どちらも実行時型情報(RTTI)が必要になるので、実装クラス側はpublished宣言でテストメソッドを定義して利用します。
テストコードの例(抜粋):
// 中略 TArgumentParserAddArgumentTest = class(TTestCase) private FArgumentParser: TArgumentParser; public procedure SetUp; override; procedure TearDown; override; published procedure TestTArgument; // 中略 procedure TArgumentParserAddArgumentTest.TestTArgument; begin FArgumentParser.AddArgument(TArgument.Create('--foo', 'bar', saStore)); AssertEquals(FArgumentParser.ArgumentsCount, 1); AssertEquals(FArgumentParser.Arguments[0].Option, '--foo'); AssertEquals(FArgumentParser.Arguments[0].Dest, 'bar'); end;
Delphi向けのTRttiContextを使った実装
Delphi向けの実装についてはTRttiContextを使って実装しています。
TRttiContextは、実行時型情報を扱うためのクラスで、メソッドオブジェクトを取り出して実行したりできます。
10年以上前ですが記事にまとめています。
FreePascal向けの実装
FreePascal向けの実装については、DelphiのTRttiContext相当のクラスが無いので、仮想メソッドテーブルからメソッドのエントリを取り出して、メソッド名を取得しています。
先月記事にまとめています。
自作のテストフレームワークを作ってみて良かったこと
自作のテストフレームワークを作った元々のモチベーションとしては、Delphiの標準のテストフレームワークであった DUnit だとXML形式でテスト結果をレポートする機能がなくて、JenkinsなどのCIツールから結果を集計するのが手軽にできなかったり、APIのスタイルで気になる部分もあり、「1ファイルで動くような手軽なテストフレームワークがあるとよいな」と思ったところからでした。
ちょうどDelphiにTRttiContextが実装されたところでもあったので、思ったより簡単にできるかも、と考えて作成してました。
FreePascal向けのPullRequestをマージしてからは、FreePascalの仕様を読みつつメンテするのも勉強になりました。
クラスシステムをメモリ上にどのように展開して処理を呼び出す、みたいな部分もイメージを持てるようになってよかったです。