FreePascalとDelphiでリフレクションとxUnitフレームワーク

DelphiとFreePascalで動作する自作ライブラリのテストコードを書くために、自作のxUnitフレームワークを使っています。

github.com

自作のテストフレームワークでは、 Test で始まるメソッド名をテストメソッドとして自動で実行するxUnitフレームワークでよくある仕様としています。

このような仕様を実装する場合に、フレームワーク側でテストコード側のメソッド名を取得するコードが必要となります。 私のテストフレームワークでは、リフレクションでこれを実装することにしました。

元々Delphiのみ対応で最初に作成したのですが、FreePascalに対応するPullRequestをもらってマージしたので、そこからはDelphi, FPC両対応としてメンテナンスしています。

DelphiとFreePascalでリフレクションの実装状況に違いがあるので、コンパイラ指令で環境ごとに処理を分けることになります。

どちらも実行時型情報(RTTI)が必要になるので、実装クラス側はpublished宣言でテストメソッドを定義して利用します。

テストコードの例(抜粋):

delphi-argparse/src/common/Nullpobug.ArgumentParserTest.pas at main · tokibito/delphi-argparse · GitHub

// 中略

  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年以上前ですが記事にまとめています。

tokibito.hatenablog.com

FreePascal向けの実装

FreePascal向けの実装については、DelphiのTRttiContext相当のクラスが無いので、仮想メソッドテーブルからメソッドのエントリを取り出して、メソッド名を取得しています。

先月記事にまとめています。

tokibito.hatenablog.com

自作のテストフレームワークを作ってみて良かったこと

自作のテストフレームワークを作った元々のモチベーションとしては、Delphiの標準のテストフレームワークであった DUnit だとXML形式でテスト結果をレポートする機能がなくて、JenkinsなどのCIツールから結果を集計するのが手軽にできなかったり、APIのスタイルで気になる部分もあり、「1ファイルで動くような手軽なテストフレームワークがあるとよいな」と思ったところからでした。

ちょうどDelphiにTRttiContextが実装されたところでもあったので、思ったより簡単にできるかも、と考えて作成してました。

FreePascal向けのPullRequestをマージしてからは、FreePascalの仕様を読みつつメンテするのも勉強になりました。

クラスシステムをメモリ上にどのように展開して処理を呼び出す、みたいな部分もイメージを持てるようになってよかったです。