Ryan McGinty
Ryan McGinty
September
8
2021

Using the DUnitX VCL GUI test runner in Delphi

DUnitX is an excellent unit test framework for Delphi created by Vincent Parrett who created the equally excellent products FinalBuilder and Continua CI. I had written my own test framework but found it lacking compared to the more fully developed DUnitX. I ended up adopting DUnitX for all my unit test projects for Delphi. The only thing I found myself missing was the GUI that I had developed for my framework. After some consideration, I decided to port what I had to work with DUnitX. I primarily created this post to address converting existing console based projects to use the GUI runner instead. Even though DUnitX is now included with Delphi, there isn’t much documentation on the steps to actually get the GUI runner up and runnning.

Option 1: Create a new project

One thing that was updated after adding the new runner was the project wizard that creates DUnitX projects. Selecting DUnitX Project from the File->New dialog in Delphi will show the wizard - which has an option for the VCL runner built-in.

DUnitX New Project Wizard

Selecting it will generate the project code necessary to use the GUI runner by default.

Option 2: Convert an existing project

Converting an existing DUnitX project to use the GUI runner is fairly easy, just not thoroughly documented.

Step 1: Include the GUI pas file in the project uses

Include DUnitX.Logger.GUI.VCL near the top of the uses clause in your project’s dpr file.

uses
SysUtils,
DUnitX.Loggers.GUI.VCL,
DUnitX.Loggers.Console,
DUnitX.Loggers.Xml.NUnit,
DUnitX.TestFramework,

Step 2: Call the Run procedure

Next, in the body of your project file (between begin and end) you’ll want to include:

    DUnitX.Loggers.GUI.VCL.Run;
exit;

The GUI runner pas file has all the logic to create the runner and start the tests in the Run procedure.

Combining with continuous integration

These days, your project is probably running under continuous integration and will be reliant on the console runner of DUnitX. Using various conditionals, you can make a project that will work for just about every use case. Below is a sample of my common boilerplate for all my DUnitX projects.

program SyncBlock.Tests;

//<SyncBlock id="OCERIS.Delphi.DUnitX.Project.AboveUses" version="Delphi.XE">
{$IFDEF CI}
{$APPTYPE CONSOLE}
{$ELSE}
{$IFNDEF GUI}
{$IFNDEF TESTINSIGHT}
{$APPTYPE CONSOLE}
{$ENDIF}
{$ENDIF}
{$ENDIF}

{$STRONGLINKTYPES ON}

{$R *.res}
//</SyncBlock>

uses
FastMM4,
SysUtils,
DUnitX.Loggers.GUI.VCL,
DUnitX.Loggers.Console,
DUnitX.Loggers.Xml.NUnit,
DUnitX.TestFramework,
//<snip> - project specific files here
CodeSiteLogging;

//<SyncBlock id="OCERIS.Delphi.DUnitX.Project.BelowUses" version="Delphi.XE">
var
runner : ITestRunner;
results : IRunResults;
logger : ITestLogger;
nunitLogger : ITestLogger;

begin
Assert.IgnoreCaseDefault := False;

{$IFDEF CI}
CodeSiteManager.Enabled := False;
{$ELSE}
{$IFDEF GUI}
DUnitX.Loggers.GUI.VCL.Run;
exit;
{$ENDIF}

{$IFDEF TESTINSIGHT}
TestInsight.DUnitX.RunRegisteredTests;
exit;
{$ENDIF}
{$ENDIF}

try
//Check command line options, will exit if invalid
TDUnitX.CheckCommandLine;
//Create the test runner
runner := TDUnitX.CreateRunner;
//Tell the runner to use RTTI to find Fixtures
runner.UseRTTI := True;
//tell the runner how we will log things
//Log to the console window
logger := TDUnitXConsoleLogger.Create(False);
runner.AddLogger(logger);
//Generate an NUnit compatible XML File
nunitLogger := TDUnitXXMLNUnitFileLogger.Create(TDUnitX.Options.XMLOutputFile);
runner.AddLogger(nunitLogger);
runner.FailsOnNoAsserts := True; //When true, Assertions must be made during tests;

//Run tests
results := runner.Execute;
if not results.AllPassed then
System.ExitCode := EXIT_ERRORS;
except
on E: Exception do
System.Writeln(E.ClassName, ': ', E.Message);
end;

{$IFNDEF CI}
//We don't want this happening when running under CI.
if TDUnitX.Options.ExitBehavior = TDUnitXExitBehavior.Pause then
begin
System.Write('Done... Press <Enter> key to quit.');
System.Readln;
end;
{$ENDIF}
//</SyncBlock>
end.

Conclusion

Switching to the GUI runner isn’t hard - you just have to know what pieces to put where. Making it co-exist with CI is also pretty simple once you have a working example. Hopefully the above information will make it a bit easier to do both.