Dunitx testing
Skill delphicleancode/delphi-spec-kit/.gemini/skills/dunitx-testing
An opinionated ecosystem of rules, skills, and steerings to elevate Delphi development to a state-of-the-art level with Artificial Intelligence.
npx -y skills add delphicleancode/delphi-spec-kit --skill dunitx-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
Unit testing patterns with DUnitX for Delphi — fixtures, mocking, integration tests
SKILL.md
6.8 KB, as published. Nobody here has run it
DUnitX Testing Patterns — Skill
Use this skill when creating or organizing unit tests in Delphi projects.
When to Use
- When creating tests for new features
- When applying TDD (Test-Driven Development)
- When creating database integration tests
- When refactoring existing tests
Test Project Structure
tests/
├── MeuApp.Tests.dpr ← Projeto de testes
├── Domain/
│ ├── MeuApp.Tests.Customer.Entity.pas
│ └── MeuApp.Tests.Order.Entity.pas
├── Application/
│ ├── MeuApp.Tests.Customer.Service.pas
│ └── MeuApp.Tests.Order.Service.pas
├── Infrastructure/
│ └── MeuApp.Tests.Customer.Repository.pas
└── Helpers/
├── MeuApp.Tests.Helpers.Database.pas
└── MeuApp.Tests.Helpers.Mocks.pas
Basic Test Fixture
unit MeuApp.Tests.Customer.Service;
interface
uses
DUnitX.TestFramework,
MeuApp.Domain.Customer.Entity,
MeuApp.Domain.Customer.Repository.Intf,
MeuApp.Application.Customer.Service;
type
[TestFixture]
TCustomerServiceTest = class
private
FService: TCustomerService;
FMockRepo: ICustomerRepository;
public
[Setup]
procedure Setup;
[TearDown]
procedure TearDown;
[Test]
procedure CreateCustomer_WithValidData_ShouldSucceed;
[Test]
procedure CreateCustomer_WithEmptyName_ShouldRaiseException;
[Test]
procedure CreateCustomer_WithDuplicateCpf_ShouldRaiseException;
[Test]
procedure GetById_WithExistingId_ShouldReturnCustomer;
[Test]
procedure GetById_WithInvalidId_ShouldRaiseNotFoundException;
end;
implementation
uses
System.SysUtils;
{ TCustomerServiceTest }
procedure TCustomerServiceTest.Setup;
begin
FMockRepo := TMemoryCustomerRepository.Create;
FService := TCustomerService.Create(FMockRepo);
end;
procedure TCustomerServiceTest.TearDown;
begin
FService.Free;
// FMockRepo é interface — liberado automaticamente
end;
[Test]
procedure TCustomerServiceTest.CreateCustomer_WithValidData_ShouldSucceed;
begin
Assert.WillNotRaiseAny(
procedure
begin
FService.CreateCustomer('João Silva', '12345678901', '[email protected]');
end
);
end;
[Test]
procedure TCustomerServiceTest.CreateCustomer_WithEmptyName_ShouldRaiseException;
begin
Assert.WillRaise(
procedure
begin
FService.CreateCustomer('', '12345678901', '[email protected]');
end,
EValidationException
);
end;
[Test]
procedure TCustomerServiceTest.CreateCustomer_WithDuplicateCpf_ShouldRaiseException;
begin
FService.CreateCustomer('João', '12345678901', '[email protected]');
Assert.WillRaise(
procedure
begin
FService.CreateCustomer('Maria', '12345678901', '[email protected]');
end,
EBusinessRuleException
);
end;
[Test]
procedure TCustomerServiceTest.GetById_WithExistingId_ShouldReturnCustomer;
var
LCustomer: TCustomer;
begin
FService.CreateCustomer('João', '12345678901', '[email protected]');
LCustomer := FService.GetById(1);
try
Assert.IsNotNull(LCustomer);
Assert.AreEqual('João', LCustomer.Name);
finally
LCustomer.Free;
end;
end;
[Test]
procedure TCustomerServiceTest.GetById_WithInvalidId_ShouldRaiseNotFoundException;
begin
Assert.WillRaise(
procedure
begin
FService.GetById(999);
end,
EEntityNotFoundException
);
end;
initialization
TDUnitX.RegisterTestFixture(TCustomerServiceTest);
end.
Mock Repository (in memory)
unit MeuApp.Tests.Helpers.Mocks;
interface
uses
System.SysUtils,
System.Generics.Collections,
MeuApp.Domain.Customer.Entity,
MeuApp.Domain.Customer.Repository.Intf;
type
TMemoryCustomerRepository = class(TInterfacedObject, ICustomerRepository)
private
FItems: TObjectList<TCustomer>;
FNextId: Integer;
public
constructor Create;
destructor Destroy; override;
function FindById(AId: Integer): TCustomer;
function FindAll: TObjectList<TCustomer>;
function FindByCpf(const ACpf: string): TCustomer;
function Exists(AId: Integer): Boolean;
procedure Insert(ACustomer: TCustomer);
procedure Update(ACustomer: TCustomer);
procedure Delete(AId: Integer);
end;
implementation
constructor TMemoryCustomerRepository.Create;
begin
inherited Create;
FItems := TObjectList<TCustomer>.Create(False);
FNextId := 1;
end;
destructor TMemoryCustomerRepository.Destroy;
begin
FItems.Free;
inherited;
end;
procedure TMemoryCustomerRepository.Insert(ACustomer: TCustomer);
begin
ACustomer.Id := FNextId;
Inc(FNextId);
FItems.Add(ACustomer);
end;
// ... demais métodos seguem o mesmo default
Test Naming Conventions
MethodName_Scenario_ExpectedBehavior
| Example | Meaning |
|---|---|
CreateCustomer_WithValidData_ShouldSucceed | Happy scenery |
CreateCustomer_WithEmptyName_ShouldRaiseException | Validation |
GetById_WithInvalidId_ShouldRaiseNotFoundException | Not found |
Delete_WhenItemHasDependencies_ShouldRaiseException | Business rule |
DUnitX Common Assertions
// Igualdade
Assert.AreEqual(Expected, Actual);
Assert.AreEqual('João', LCustomer.Name);
// Nulidade
Assert.IsNotNull(LCustomer);
Assert.IsNull(LResult);
// Booleanos
Assert.IsTrue(LCustomer.IsActive);
Assert.IsFalse(LOrder.IsCancelled);
// Exceptions
Assert.WillRaise(AnonProc, EValidationException);
Assert.WillNotRaiseAny(AnonProc);
// Contagem
Assert.AreEqual(3, LList.Count);
Integration Test with SQLite in Memory
[TestFixture]
TCustomerRepositoryIntegrationTest = class
private
FConnection: TFDConnection;
FRepository: ICustomerRepository;
public
[Setup]
procedure Setup;
[TearDown]
procedure TearDown;
end;
procedure TCustomerRepositoryIntegrationTest.Setup;
begin
FConnection := TFDConnection.Create(nil);
FConnection.DriverName := 'SQLite';
FConnection.Params.Database := ':memory:';
FConnection.Connected := True;
// Criar schema
FConnection.ExecSQL(
'CREATE TABLE customers (' +
' id INTEGER PRIMARY KEY AUTOINCREMENT,' +
' name TEXT NOT NULL,' +
' cpf TEXT NOT NULL,' +
' email TEXT,' +
' status INTEGER DEFAULT 0' +
')'
);
FRepository := TFireDACCustomerRepository.Create(FConnection);
end;
procedure TCustomerRepositoryIntegrationTest.TearDown;
begin
FConnection.Free;
end;
Test Checklist
- Test name follows
MethodName_Scenario_ExpectedBehavior? - Clean setup and TearDown without side effects?
- Test checks ONE thing?
- Objects released correctly in TearDown and in try/finally?
- Mock repository used for Service testing?
- SQLite
:memory:used for Repository integration testing? - Test happy scenario AND error scenarios?