name: Delphi Test-Driven Development (TDD) and DUnitX description: Guidelines on how the AI should act and code when the user requests TDD, unit tests, DUnitX or fakes/mocks using Interfaces in Delphi.
Test-Driven Development (TDD) in Delphi with DUnitX
This skill guides behavioral expectations for test-driven development (TDD) using the modern Delphi ecosystem.
When operating under the scope of TDD, AI MUST ALWAYS prioritize the Red-Green-Refactor cycle. If the user requests TDD, DO NOT write the business implementation before writing the test that will fail.
The Red-Green-Refactor Cycle (Interaction Rules)
- Red (Failed Test): Start by declaring the skeleton of the target class/interface in the
interfacesection just to compile. Then, immediately write a complete DUnitX Test Case calling out the non-existent behavior or asserting an expected result. The test will logically fail. - Green (Minimal Code): Write the minimum and raw actual implementation, enough to make the test
Assertpass. - Refactor (Cleaning): Improve the code (Clean Code, duplication removal, optimizations) ensuring that the test does not break.
DUnitX Best Practices
Test Structure
- Test Class: The test case must be annotated with
[TestFixture]. - Setup and TearDown: Use
[Setup]to instantiate Classes and Fakes. Use[TearDown]to clean up instances that do not use ARC (Interfaces). - No Memory Leaks in Tests:
[TearDown]and injection via Interface (ARC) are mandatory to keep the suite watertight.
Nomenclature of Test Methods
Embrace context conventions like Action_Condition_ExpectedResult:
[Test]
procedure ComputeDiscount_LoyalCustomer_ReturnsTenPercent;
Modern Assertions
Replace manual Boolean validations (Assert.IsTrue(A = B)) with fluent and specific Assert:
Assert.AreEqual(100.0, FInvoice.Total)Assert.IsNotNull(FCustomer)Assert.WillRaise(procedure begin FSut.DoInvalid; end, EBusinessRuleException)Assert.Contains('Error', LMessage)
Dependency Injection and Mocks (Test Doubles)
To isolate the class under test (SUT - System Under Test) from the infrastructure (Database, APIs, View), apply Strict Dependency Inversion (DIP) by injecting Interfaces into the SUT via constructor.
As Delphi does not have a built-in Mocking Framework in RTL, write local "Fake/Mock" Classes implementing the Interface to simulate the dependency within the test's implementation session.
// Cria-se o Fake only no arquivo de teste
TFakeEmailService = class(TInterfacedObject, IEmailService)
public
SentCount: Integer;
procedure Send(const AMsg: string);
end;
AntiPatterns that AI should Avoid
- ❌ Direct coupling to the database (
TFDQuery) in the tested class. Always abstract database access into aIRepositoryand create aTFakeRepositoryfor DUnitX. - ❌ Test UI. Restrict the scope of DUnitX to the Domain and Application Services Layer.
- ❌ Write the entire class along with the tests. The user must be followed step-by-step in TDD. If the AI is required to provide everything, send the Tests first in the output.
- ❌ Swallowing Exceptions (
try..except on E: Exception do) in methods being tested, this breaksAssert.WillRaise().