ttestsetupクラスでttestcaseのフィールドにアクセスする方法
-
29-10-2019 - |
質問
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スタイルになります。)