A few notes I wrote down when coaching a team to write better unit tests and test tooling form themselves.
- unit tests test a unit of code
- integrating tests test multiple units of code, which can go as far as having external dependencies
- mocks simulate depencencies
- unit tests are being executed by a unit test runner
- you can group tests into test suites, which can contain other suites, and determine order of tests (which can be important for integration tests).
- unit tests and suites register them to be eligible for running (a test without an encompassing suite presents itself as a suit with one test)
- the runner optionally has a mechanism to filter the eligible suites and tests down to the ones actually being run
References:
- Unit testing – Wikipedia
- [WayBack] UnitTest – Martin Fowler: Unit Tests are focused on small parts of a code-base, defined in regular programming tools, and fast. There is disagreement on whether units should be solitary or sociable.
In the particular case for this team, testing was mostly done using DUnit for Delphi.
Here, these are worth mentioning:
- The configuration is not limited to the
GuiTestRunner
: any DUnit based test runner can use it (though the default consoleTextTestRunner
skips it, but https://github.com/graemeg/fptest/blob/master/src/TextTestRunner.pas and https://github.com/VSoftTechnologies/DUnit-XML/blob/master/Example/DUnitXMLTest.dpr shows how it can be used).- It comes down to either
Suite.LoadConfiguration(IniFileName, UseRegistry, True)
orRegisteredTests.LoadConfiguration(IniFileName, UseRegistry, True)
whereIniFileName
contains the INI filename, for instance fromExtractFilePath(ParamStr(0)) + 'dunit.ini'
or from aParamStr
parameter on the command-line.UseRegistry
usually isFalse
- It comes down to either
- If you want to disable all exceptions for easier debugging, but still want to catch failures, then you can enable
Break on Failures
(see screenshot below) so breaking tests will throw anEBreakingTestFailure
. - Registration
- Per test or per suite
- You do not need a
ITestSuite
implementing class in order to register a suite (just pass aSuitePath
when registering multiple tests) - Basically the only reasons for having a
ITestSuite
implementing class (like descending fromTTestSuite
) are- to have a specific
SetUp
orTearDown
for that suite level - to allow
\
backslash or/
forward slash in test suite names (which is unwise because a lot of tooling sees those as suite hierarchy separators)
- to have a specific
function TestSuite(AName: string; const Tests: array of ITest): ITestSuite;
procedure RegisterTest(SuitePath: string; test: ITest); overload;
procedure RegisterTest(test: ITest); overload;
procedure RegisterTests(SuitePath: string; const Tests: array of ITest); overload;
procedure RegisterTests(const Tests: array of ITest); overload;
function RegisteredTests: ITestSuite;
- Configuration is exclusion based
procedure TTestSuite.LoadConfiguration(const iniFile: TCustomIniFile; const section: string);
procedure TTestSuite.SaveConfiguration(const iniFile: TCustomIniFile; const section: string);
- The configuration file default name is
DUnit.ini
- The
DUnit.ini
file will be saved after the GUI tests are run (overwriting any changes) when theAuto Save Configuration
is enabled (which is the default)
- All tests are configured in
- sections
- named (of course inside
[]
brackets) asTests.TestPath
, whereTestPath
either- is the name of the test class
- is a
.
period separated path of suites ending in an test class
- values having keys named either
- the test method with a value
0
to disable the test - a test method followed by
.RunCount
with an integer value indicating how often that test needs to be executed
- the test method with a value
- note that with either
TestName=1
orTestName.RunCount=1
will disappear from the ini file because those are default values
- named (of course inside
- There are no values to indicate tests need to be run (so by default registered tests eligible to be run are being run)
- sections
- An example file (without
.RunCount
) is at [WayBack] delphidicontainer/dunit.ini at master · danieleteti/delphidicontainer · GitHub - You can add comments to INI files using a semi colon at the start of the line; see [WayBack] Do standard windows .ini files allow comments? – Stack Overflow
Registration and exclusion are two separate concerns.
To configure non-GUI tests, first run the GUI tester, configure it, then copy the resulting DUnit.ini file to the environment where the non-GUI tests are being run.
Be sure to check out test decorators, and maybe amend them with dependency injection. Example for apply database setup/teardown to a full suite of tests: [WayBack] How to organize testing of DB in dUnit — IT daily blog, news, magazine, technologies
Some resurrected documentation links because not all links from [WayBack] DUnit – Xtreme testing for Delphi and [WayBack] DUNIT: An Xtreme testing framework for Delphi programs succeed.
- Via [WayBack] DUnit – Xtreme testing for Delphi :
- [WayBack] Xtreme testing for Delphi programs still exists and refers to [WayBack] Xtreme testing
- WayBack: Test Infected: now at [WayBack] Test Infected:
- WayBack: Beck Testing Framework
- Via [WayBack] DUNIT: An Xtreme testing framework for Delphi programs:
- WayBack: API documentation generated by Time2Help
- Unarchived:
- “The Case for XP” part 1 part 2 have now been combined into [WayBack] cLabs: The case for XP
- WayBack: The Montebello Consultancy; WayBack: Java+JUnit article by William C. Wake (now at[WayBack] The Test/Code Cycle in XP, Part 1: Model – XP123);[WayBack] Free Downloads:Delphi+DUnit in WayBack: DSearchTest.zip and WayBack: DSearchGUITest.zip
–jeroen