For unit testing to get off the ground without a complete rewrite of all the singletons you have, I have found that just adding the ability to Free
and re-Create
the singletons is usually more than enough to get you started.
Assuming the singletons are instantiated in an initialization section (Ban those. They are the bane of any unit test. Go for separate registration and initialization units.), you can simply add two procedures to the interface section. Put them between conditional defines if you don't want other units in your normal project to use them:
{$IFDEF DUNIT}
procedure InstantiateMySingleton;
procedure FreeMySingleton;
{$ENDIF}
Move the code that you now have in your initialization and finalization sections to the implementation of these procedures and just call them from the initialization and finalization.
procedure InstantiateMySingleton;
begin
// ...
end;
procedure FreeMySingleton;
begin
// ...
end;
initialization
InstantiateMySingleton;
finalization
FreeMySingleton;
With this done you can start to use the InstantiateMySingleton and FreeMySingleton in your unit tests' setup and teardown methods.
All you have then left to do is to make sure that creating and destroying the singleton doesn't leak memory and is something that can actually be repeated with the same functional results every single time. One thing I have found that helps in ensuring this is to use the GUI runner and run the whole test suite twice (without exiting the GUI of course!). If there are tests that succeed on the first run and fail on the second run, you have problems in the initialization or finalization of your singleton.