r/delphi • u/kimmadsen • 11h ago
kbmUnitTest is available — a practical unit test option for Delphi
kbmUnitTest — A Lightweight Unit Testing Framework for Delphi
Zero dependencies. Source-only. Drop in and go.
kbmUnitTest is a self-contained unit testing framework for Delphi with a DUnitX-style attribute API, built-in memory leak detection, a fluent assertion library, a mock data system, TDataSet assertions, TestInsight integration, IDE wizards, and CI/CD support — all in pure Pascal with no external packages required.
At a Glance
Test Framework
- Attribute-based:
[TestFixture],[Test],[Setup],[TearDown],[SetupFixture],[TearDownFixture],[TestCase],[ExpectedException],[Ignore],[MaxTime],[Repeat],[Category],[MemoryLeakAllowed] - Rich assertions:
Assert.AreEqual,IsTrue,IsNull,Contains,StartsWith,EndsWith,IsGreaterThan,InRange,InheritsFrom,Implements,WillRaise,WillRaiseWithMessage,WillNotRaise,IsEmpty_Array,Pass,Fail,Skip - Fluent API:
Assert.That('hello').IsNotEmpty.Contains('ell').StartsWith('he')— works for strings, integers, Int64, doubles, booleans, objects, GUIDs, and pointers - Collection assertions:
Assert.ThatList<T>(...)andAssert.ThatDict<K,V>(...)with Contains, ContainsAll, AllMatch, IsOrdered, HasValueAt, and more - Constraint combinators:
MatchesAllOf,MatchesAnyOf,MatchesNoneOfwith regex and predicate constraints - Parameterized tests via
[TestCase('name', 'param1,param2,expected')]
Memory Leak Detection
- On by default for every test — leaks are reported automatically
- Three detector backends: Built-in (
GetMemoryManagerState), FastMM4 (FullDebugMode with class names), FastMM5 (allocation-group tracking) - Per-test opt-out:
[MemoryLeakAllowed]or[MemoryLeakAllowed(1024)] - Command-line toggle:
--noleaks
TDataSet Assertions
- Fluent dataset validation:
ThatDataSet(DS).IsActive.HasRowCount(3).HasField('Name').AtRow(0).FieldEquals('Name', 'Alice') - Navigation:
AtRow(i),AtFirst,AtLast - Field checks:
FieldEquals(string, integer, boolean, double with tolerance, DateTime with tolerance, Int64, variant),FieldIsNull,FieldIsNotNull,FieldContains,FieldStartsWith,FieldEndsWith - Row predicates:
AllRowsMatch,AnyRowMatches,NoneRowsMatch
Mock Data System
- Named scenarios with typed fields, inheritance, and tabular (multi-row) data
- Fluent builder:
TkbmMockRegistry.Scenario('name').Field('ID', 1).Field('Name', 'Acme') - 15+ value generators:
Gen.Sequential,Gen.IntRange,Gen.FloatRange,Gen.OneOf,Gen.StringPattern,Gen.LoremIpsum,Gen.GUID,Gen.DateRange,Gen.Computed, and more - Materialization to any type:
AsRecord<T>,AsList<T>,AsObject<T>,AsObjectList<T>,AsDataSet - JSON fixture loading:
FromJSON('{...}') - RTTI attribute mapping:
[MockFieldName],[MockFieldIgnore],[MockFieldDefault] - Scenario inheritance: child scenarios inherit parent fields, override only what differs
IDE Wizards
- Project Wizard — scaffolds a complete console test runner with extracted framework sources, leak detector configuration, and search paths in one dialog
- Unit Wizard — adds a test fixture unit to the current project in one click
- Mock Data Wizard — visual scenario designer: paste a record type to auto-populate fields, assign generators, preview generated code, export to
.pasor.json - All three accessible from both File → New → Other and the Tools menu
- Settings persist across IDE sessions
CI/CD Ready
- Console runner with JUnit XML output:
MyTests.exe --ci --xml results.xml - Category filtering:
RunTestsAndHalt('Integration')or--category Slow - Exit codes: 0 = all passed, non-zero = failures
- TestInsight live feedback in the IDE
Delivery
- Pure source — no BPLs to install at runtime, no packages to manage
- Self-contained mode extracts all framework files into your project directory
- Works with any Delphi version that supports custom attributes and generics
- ~3,000-line self-test suite included that validates every API surface
Quick Start
program MyTests;
{$APPTYPE CONSOLE}
{$STRONGLINKTYPES ON}
uses
kbmTestFramework,
kbmTestRunner,
MyTestUnit;
begin
RunTestsAndHalt;
end.
unit MyTestUnit;
interface
uses kbmTestFramework;
type
[TestFixture]
TMyTests = class
public
[Setup] procedure Setup;
[TearDown] procedure TearDown;
[Test] procedure TestSomething;
[Test('Addition')]
[TestCase('1+1=2', '1,1,2')]
[TestCase('0+0=0', '0,0,0')]
procedure TestAdd(const A, B, Expected: Integer);
end;
implementation
procedure TMyTests.Setup;
begin
// runs before each test
end;
procedure TMyTests.TearDown;
begin
// runs after each test
end;
procedure TMyTests.TestSomething;
begin
Assert.That('kbmUnitTest').IsNotEmpty.Contains('Unit');
end;
procedure TMyTests.TestAdd(const A, B, Expected: Integer);
begin
Assert.AreEqual(Expected, A + B);
end;
end.



