... type TTestDB1 = class(TTestCase) protected public procedure SetUp; override; procedure TearDown; override; published procedure TestDB1_1; procedure TestDB1_2; end; ... implementation ... procedure TTestDB1.SetUp; begin inherited; // connect to DB end; procedure TTestDB1.TearDown; begin // disconnect from DB inherited; end; ... initialization RegisterTest(TTestDB1.Suite); end. 
-- TTestDB1.SetUp ---- TTestDB1.TestDB1_1 -- TTestDB1.TearDown -- TTestDB1.SetUp ---- TTestDB1.TestDB1_2 -- TTestDB1.TearDown ITest interface as TTestCase, that is, the same scheme: SetUp, Test ..., TearDown, only instead of calling the tests, the entire test-case specified during its creation is called. Those. modifying the module: uses ... TestExtensions; type TTestDBSetup = class(TTestSetup) public procedure SetUp; override; procedure TearDown; override; // published- TTestSetup end; TTestDB1 = ... ... implementation ... initialization RegisterTest(TTestDBSetup.Create(TTestDB1.Suite)); end. -- TTestDBSetup.SetUp ---- TTestDB1.SetUp ------ TTestDB1.TestDB1_1 ---- TTestDB1.TearDown ---- TTestDB1.SetUp ------ TTestDB1.TestDB1_2 ---- TTestDB1.TearDown -- TTestDBSetup.TearDown 
TTestSetup.Create described as follows: constructor TTestSetup.Create(ATest: ITest; AName: string = ''); RegisterTest(TTestDBSetup.Create(TTestDB1.Suite)); RegisterTest(TTestDBSetup.Create(TTestDB2.Suite)); -- TTestDBSetup.SetUp ---- TTestDB1.SetUp ------ TTestDB1.TestDB1_1 ---- TTestDB1.TearDown ---- TTestDB1.SetUp ------ TTestDB1.TestDB1_2 ---- TTestDB1.TearDown -- TTestDBSetup.TearDown -- TTestDBSetup.SetUp ---- TTestDB2.SetUp ------ TTestDB2.TestDB2_1 ---- TTestDB2.TearDown ---- TTestDB2.SetUp ------ TTestDB2.TestDB2_2 ---- TTestDB2.TearDown -- TTestDBSetup.TearDown 
procedure RegisterTest(SuitePath: string; test: ITest); begin assert(assigned(test)); if __TestRegistry = nil then CreateRegistry; RegisterTestInSuite(__TestRegistry, SuitePath, test); end; SuitePath ? We look RegisterTestInSuite : procedure RegisterTestInSuite(rootSuite: ITestSuite; path: string; test: ITest); ... begin if (path = '') then begin // End any recursion rootSuite.addTest(test); end else begin // Split the path on the dot (.) dotPos := Pos('.', Path); if (dotPos <= 0) then dotPos := Pos('\', Path); if (dotPos <= 0) then dotPos := Pos('/', Path); if (dotPos > 0) then begin suiteName := Copy(path, 1, dotPos - 1); pathRemainder := Copy(path, dotPos + 1, length(path) - dotPos); end else begin suiteName := path; pathRemainder := ''; end; ... RegisterTest('Setup decorator ((d) TTestDB1)', TTestDB2.Suite); 
RegisterTestInSuite code: procedure RegisterTestInSuite(rootSuite: ITestSuite; path: string; test: ITest); ... begin ... currentTest.queryInterface(ITestSuite, suite); if Assigned(suite) then begin ... ... function DBSuite: ITestSuite; begin Result := TTestSuite.Create('DB tests'); Result.AddTest(TTestDB1.Suite); Result.AddTest(TTestDB2.Suite); end; ... initialization RegisterTest(TTestDBSetup.Create(DBSuite)); 
... function DBSuite: ITestSuite; begin ... Result.AddTest(TTestDB3.Suite); end; ... function TTestSetup.GetName: string; begin Result := Format(sSetupDecorator, [inherited GetName]); end; AName in constructor TTestSetup.Create(ATest: ITest; AName: string = ''); constructor TAbstractTest.Create(AName: string); ... FTestName := AName; ... ... TTestDBSetup = ... public function GetName: string; override; ... implementation ... function TTestDBSetup.GetName: string; begin Result := FTestName; end; ... initialization RegisterTest(TTestDBSetup.Create(DBSuite, 'DB')); 
unit uTestDB3; ... initialization RegisterTest('DB', TTestDB3.Suite)); RegisterTestInSuite ) for TTestDBSetup to implement the ITestSuite interface. ... ITestSuite = interface(ITest) ['{C20E38EF-7369-44D9-9D84-08E84EC1DCF0}'] procedure AddTest(test: ITest); procedure AddSuite(suite : ITestSuite); end; ... TTestDBSetup = class(TTestSetup, ITestSuite) public procedure AddTest(test: ITest); procedure AddSuite(suite : ITestSuite); end; ... implementation ... procedure TTestDBSetup.AddTest(test: ITest); begin Assert(Assigned(test)); FTests.Add(test); end; procedure TTestDBSetup.AddSuite(suite: ITestSuite); begin AddTest(suite); end; ... 

procedure TTestDecorator.RunTest(ATestResult: TTestResult); begin FTest.RunWithFixture(ATestResult); end; FTest ), which were set when creating TTestDBSetup: constructor TTestDecorator.Create(ATest: ITest; AName: string); begin ... FTest := ATest; FTests:= TInterfaceList.Create; FTests.Add(FTest); end; FTests ) - no. Launch them by redefining RunTest: ... TTestDBSetup = ... protected procedure RunTest(ATestResult: TTestResult); override; ... end. ... procedure TTestDBSetup.RunTest(ATestResult: TTestResult); var i: Integer; begin inherited; // , .. FTest for i := 1 to FTests.Count - 1 do (FTests[i] as ITest).RunWithFixture(ATestResult); end; 
... TTestDBSetup = ... protected ... function CountTestInterfaces: Integer; function CountEnabledTestInterfaces: Integer; public ... function CountTestCases: Integer; override; function CountEnabledTestCases: Integer; override; end; ... function TTestDBSetup.CountTestCases: Integer; begin Result := inherited; if Enabled then Inc(Result, CountTestsInterfaces); end; function TTestDBSetup.CountTestInterfaces: Integer; var i: Integer; begin Result := 0; // skip FIRST test case (it is FTest) for i := 1 to FTests.Count - 1 do Inc(Result, (FTests[i] as ITest).CountTestCases); end; function TTestDBSetup.CountEnabledTestCases: Integer; begin Result := inherited; if Enabled then Inc(Result, CountEnabledTestInterfaces); end; function TTestDBSetup.CountEnabledTestInterfaces: Integer; var i: Integer; begin Result := 0; // skip FIRST test case (it is FTest) for i := 1 to FTests.Count - 1 do if (FTests[i] as ITest).Enabled then Inc(Result, (FTests[i] as ITest).CountTestCases); end; ... 

TTestDBSetup (renamed to TTestSetupEx ) is moved to a separate project dUnitEx (see TestSetupEx.pas )Source: https://habr.com/ru/post/266487/
All Articles