Delphi-spec-kit Delphi Test-Driven Development (TDD) and DUnitX
Guidelines on how the AI should act and code when the user requests TDD, unit tests, DUnitX or fakes/mocks using Interfaces in Delphi.
git clone https://github.com/delphicleancode/delphi-spec-kit
T=$(mktemp -d) && git clone --depth=1 https://github.com/delphicleancode/delphi-spec-kit "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/tdd-dunitx" ~/.claude/skills/delphicleancode-delphi-spec-kit-delphi-test-driven-development-tdd-and-dunitx && rm -rf "$T"
.claude/skills/tdd-dunitx/SKILL.mdTest-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
section 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.interface - Green (Minimal Code): Write the minimum and raw actual implementation, enough to make the test
pass.Assert - 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
to instantiate Classes and Fakes. Use[Setup]
to clean up instances that do not use ARC (Interfaces).[TearDown] - No Memory Leaks in Tests:
and injection via Interface (ARC) are mandatory to keep the suite watertight.[TearDown]
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.
//Fake is created only in the test file TFakeEmailService = class(TInterfacedObject, IEmailService) public SentCount: Integer; procedure Send(const AMsg: string); end;
AntiPatterns that AI should Avoid
- ❌ Direct coupling to the database (
) in the tested class. Always abstract database access into aTFDQuery
and create aIRepository
for DUnitX.TFakeRepository - ❌ 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 (
) in methods being tested, this breakstry..except on E: Exception do
.Assert.WillRaise()