質問

Dunitでユニットテストを作成しています。初期化にはかなり長い時間がかかるクラスがあります。

TtestSetupからクラスTMYTESTSETUPを導き出し、セットアップ方法をオーバーライドします。このセットアップ方法は、私のttestcase内のすべてのテストに対して1回のみ呼び出されます。パフォーマンスを向上させるために、初期化プロセスをtmyTestSetup.setupルーチンに配置します。

私の問題は、初期化するオブジェクトにアクセスするにはどうすればよいですか。これは、テストセットアップクラスのTMYTESTのフィールドです。グローバルに宣言する唯一の方法はありますか?

テストされていない短い例:

TMyTestSetup = class(TTestSetup)
  protected
    procedure SetUp; override;
end;

TMyTest = class(TTestcase)
public
    fTakes4Ever2Init : TInits4Ever2Init;
published
  procedure Test1;     
end;

implementation

procedure TMyTestSetup.Setup;
begin
   // How can I access fTakes4Ever2Init from here?
  fTakes4Ever2Init.create // This is the call that takes long
end;

procedure TMyTest.Test1;
begin
  fTakes4Ever2Init.DoSomething;
end;

initialization
  RegisterTest(TMyTestSetup.Create(TMyTest.Suite));
役に立ちましたか?

解決

トリックは、TMYTestSetupクラスでパブリッククラス変数を使用することです。

このように(テストと動作、完全)例:

unit TestTestUnit;

interface

uses
  TestFramework, TestExtensions;

type
  TInits4Ever2Init = class
  private
    FValue: integer;
  public
    constructor Create;
    procedure   DoSomething1;
    procedure   DoSomething2;
    procedure   DoSomething3;
  end;

type
  TMyTestSetup = class(TTestSetup)
  public class var
    fTakes4Ever2Init: TInits4Ever2Init;
  protected
    procedure SetUp; override;
  end;

  TMyTest = class(TTestCase)
  published
    procedure Test1;
    procedure Test2;
    procedure Test3;
  end;

implementation

uses
  SysUtils, Windows;

{ TMyTestSetup }

procedure TMyTestSetup.Setup;
begin
  fTakes4Ever2Init := TInits4Ever2Init.create; // This is the call that takes long
end;

{ TMyTest }

procedure TMyTest.Test1;
begin
  TMyTestSetup.fTakes4Ever2Init.DoSomething1;
end;

procedure TMyTest.Test2;
begin
  TMyTestSetup.fTakes4Ever2Init.DoSomething2;
end;

procedure TMyTest.Test3;
begin
  TMyTestSetup.fTakes4Ever2Init.DoSomething3;
end;

{ TInits4Ever2Init }

constructor TInits4Ever2Init.Create;
begin
  inherited Create;

  // FValue and Format('%p, %d', [Pointer(Self), FValue])) are to confirm
  //   that we are talking to the same object for all the tests,
  //   but that the object is different each time we run the test suite.

  Randomize;
  FValue := Random(10000);

  OutputDebugString(pAnsiChar('-- TInits4Ever2Init.Create: '
    + Format('%p, %d', [Pointer(Self), FValue])));
end;

procedure TInits4Ever2Init.DoSomething1;
begin
  OutputDebugString(pAnsiChar('-- TInits4Ever2Init.DoSomething1: '
    + Format('%p, %d', [Pointer(Self), FValue])));
end;

procedure TInits4Ever2Init.DoSomething2;
begin
  OutputDebugString(pAnsiChar('-- TInits4Ever2Init.DoSomething2: '
    + Format('%p, %d', [Pointer(Self), FValue])));
end;

procedure TInits4Ever2Init.DoSomething3;
begin
  OutputDebugString(pAnsiChar('-- TInits4Ever2Init.DoSomething3: '
    + Format('%p, %d', [Pointer(Self), FValue])));
end;

initialization
  RegisterTest(TMyTestSetup.Create(TMyTest.Suite));
end.

サンプルのコメントが示すように、私はランダム化されたプライベート変数といくつかのデバッグトレース出力を使用して、テストスイートとの各テスト呼び出しがターゲットオブジェクトの同じコピーにあるが、別のコピーを取得していることを確認しましたテストスイートが実行されるたびにターゲットオブジェクトの。

他のヒント

TtestSuiteクラスから新しいテストスイートクラスを導き出し、セットアップと断脱走方法をオーバーライドすると、この特定のテストスイートにテストケースを追加して、スイートを登録できます。

このようにして、テストスイートクラスのセットアップと分解メソッドが1回呼び出され、各テストケースのセットアップおよび断片化方法は、そのテストケースで定義されているすべてのテスト方法に対して呼び出されます。

実行注文は次のようになります:

TestSuite.SetUp;

-- TestCase1.Setup;
---- TestCase1.Test1;
-- TestCase1.TearDown;
-- TestCase1.Setup;
---- TestCase1.Test2;
-- TestCase1.TearDown;

-- TestCase2.Setup;
---- TestCase2.Test1;
-- TestCase2.TearDown;
-- TestCase2.Setup;
---- TestCase2.Test2;
-- TestCase2.TearDown;

-- TestCaseN.Setup;
---- TestCaseN.Test1;
-- TestCaseN.TearDown;
-- TestCaseN.Setup;
---- TestCaseN.Test2;
-- TestCaseN.TearDown;

TestSuite.TearDown;

公開された方法が1つだけで、他のすべてのテスト方法を呼び出すことは、セットアップと分解手順を1回だけ呼び出すための怠zyであるが速い方法です。

テストスイート全体のttestcaseフィールドを初期化することはできません。ここに説明があります。

unit Tests3;

interface

uses
  TestFramework, TestExtensions, Windows, Forms, Dialogs, Controls, Classes,
  SysUtils, Variants, Graphics, Messages;

type
  TMyTestCase = class(TTestCase)
  private
    FValue: Integer;
  published
    procedure Test1;
    procedure Test2;
  end;

implementation

{ TMyTestCase }

procedure TMyTestCase.Test1;
begin
  FValue:= 99;
  ShowMessage(Format('%p, %d', [Pointer(Self), FValue]));
end;

procedure TMyTestCase.Test2;
begin
  ShowMessage(Format('%p, %d', [Pointer(Self), FValue]));
end;

initialization
  RegisterTest(TMyTestCase.Suite);
end.

上記のユニットテストを実行すると、test1およびtest2メソッドに示されている「自己」アドレスが異なることがわかります。つまり、TMYTestCaseオブジェクトインスタンスはTEST1およびTEST2呼び出しで異なります。

その結果、TMYTestCaseクラスで宣言する可能性のあるフィールドは、テスト方法の呼び出しの間で揮発性です。

「グローバル」初期化を実行するには、TMYTestcaseフィールドとしてではなく、オブジェクトをグローバルに宣言する必要があります。

使用 TTestSetup あなたはこのようなことをすることができます:

type
  TMyTestSetup = class(TTestSetup)
  private
    FValue: Integer;
  protected
    procedure SetUp; override;
    procedure TearDown; override;
  end;

  TMyTestCase = class(TTestCase)
  published
    procedure TestSomething;
  end;

var
  TestSetup: TMyTestSetup;

procedure TMyTestSetup.SetUp;
begin
  inherited;
  TestSetup := Self;
  FValue := 42;
end;

procedure TMyTestSetup.TearDown;
begin
  TestSetup := nil;
  inherited;
end;

procedure TMyTestCase.TestSomething;
begin
  CheckEquals(TestSetup.FValue, 42);
end;

initialization
  TestFramework.RegisterTest(TMyTestSetup.Create(
    TTestSuite.Create('My test suite', [TMyTestCase.Suite])
  ));

それはあなたの心をやや反抗していると感じますが、それは仕事をします!

Delphiバージョンによっては、単に TMyTest.fTakes4Ever2Init フィールドa public class var テストセットアップから初期化します。 (これは、ユニットグローバル変数と比較して、よりOOPスタイルになります。)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top