New DUnitLite feature: Test insulation with TInsulatedTest and TSpecification

DUnitLite 0.3 (download) has a new feature: test insulation. Test-case instances live only as long as it takes to run them.

Here’s a quick illustration of the difference. Given this test case:

TestFrabjulizer = class(TTestCase)
procedure TestFoo;
procedure TestBar;
procedure TestBaz;

With out-of-the-box DUnit, you’d see something like this:

  • Three instances of TestFrabjulizer are created, one for each test.
  • The DUnit GUI is displayed.
  • You select some tests and press Play.
  • The existing TestFrabjulizer instances are executed in turn.
  • You close the program. The TestFrabjulizer instances are freed.

But if instead of TTestCase, you descend from DUnitLite’s new TInsulatedTest, the behavior looks like this instead:

  • The DUnit GUI is displayed.
  • You select some tests and press Play.
  • An instance of TestFrabjulizer is created for TestFoo, executed, and freed.
  • An instance of TestFrabjulizer is created for TestBar, executed, and freed.
  • An instance of TestFrabjulizer is created for TestBaz, executed, and freed.
  • You close the program.

This scheme has several benefits over DUnit’s default behavior:

Interfaces work the way you expect. You can put interface-reference fields on your test case, and they’ll be automatically freed when the test is done — just like the way you’re used to them getting freed when you’re done using any other object.

TestFrabjulizer = class(TTestCase)
strict private
FFrabjulizer: IFrabjulizer;

procedure TestFrabjulizer.SetUp;
inherited SetUp;
FFrabjulizer := TFrabjulizer.Create;
// With TTestCase, you need to nil FFrabjulizer in TearDown.
// With TInsulatedTest, you don't: it'll go away automatically.

Inline records and arrays are practical. Much like the previous item, but instead of IFrabjulizer, use TMyBigRecord or array [0..3] of array [0..1023] of Double. With TTestCase, this is a quick way to make your test app run out of memory, especially if you have thousands of tests like we do. With TInsulatedTest, it’s a handy way to simplify your test: no mucking around with GetMem and FreeMem, and the memory is automatically initialized to all zero bytes.

Tests can’t interact with themselves. If you run a test twice in a row, it gets clean instance variables each time, so there’s no chance of problems due to dangling state that you forgot to zero out in SetUp. (Of course, global variables can still screw you up.)


There’s a lot less to say here, but 0.3 also introduces a new TSpecification base class.

uses Specifications;

FrabjulizerSpec = class(TSpecification)

TSpecification is really just TInsulatedTest, with very little added. Mostly this is to help stick with the “specification” mindset, rather than having a specification that descends from something with “test case” in its name.

TSpecification also, through a type-aliasing trick, makes Specify.That and Should... available to all of its descendants, without those descendant units needing to explicitly use the Specifiers unit. It’s kind of cute. (Specify and Should are, of course, still available outside of TSpecification.)

Leave a Reply

Your email address will not be published. Required fields are marked *