Search code examples
delphidunitdelphi-mocks

Delphi Mocks lifecycle in DUnit


I want to test the simplest case : testing a mocked strategy object. (look at : Strategy pattern).

If I create a TMock<T> in the TTestCase.setUp methods and store it in a TTestCase instance attribute, then should I free/NIL the mock variable in the tearDown method?

The mock := NIL does not compile:

[dcc32 Error] TestUnit2.pas(44): E2010 Incompatible types: 'Delphi.Mocks.TMock<T>' and 'Pointer'.

The mock.free runs without any error, but I'm not sure I should call it. The mock released when the process exits its scope (after test case destructor).

Should I call/set anything?

The code:

Unit2.pas:

unit Unit2;

interface

type
  TPartClass = class
  public
    function foo( x_ : integer ) : integer; virtual;
  end;

  TMainClass = class
  private
    fPart : TPartClass;
  public
    constructor create( part_ : TPartClass );

    function bar( x_ : integer ) : integer;
  end;

implementation

function TPartClass.foo( x_ : integer ) : integer;
begin
  result := x_ shl 1;
end;

constructor TMainClass.create( part_ : TPartClass );
begin
  inherited create;
  fPart := part_;
end;

function TMainClass.bar( x_ : integer ) : integer;
begin
  result := fPart.foo( x_ );
end;

TestUnit2.pas:

unit TestUnit2;

interface

uses
  Delphi.Mocks, TestFramework, Unit2;

type
  TTestTMainClass = class(TTestCase)
  strict private
    fPartClass : TMock<TPartClass>;
    FMainClass: TMainClass;
  public
    procedure SetUp; override;
    procedure TearDown; override;
  published
    procedure Testbar;
  end;

implementation

procedure TTestTMainClass.SetUp;
begin
  fPartClass := TMock<TPartClass>.create;
  FMainClass := TMainClass.Create( fPartClass );
end;

procedure TTestTMainClass.TearDown;
begin
  FMainClass.Free;
  FMainClass := NIL;
  //fPartClass.Free;
  //fPartClass := NIL;
end;

procedure TTestTMainClass.Testbar;
var
  ReturnValue: Integer;
  x_: Integer;
begin
  fPartClass.Setup.WillReturn( 10 ).When.foo( 5 );
  x_ := 5;
  ReturnValue := FMainClass.bar(x_);
  checkTRUE( returnValue = 10 );
end;

Solution

  • You should always cleanup everything in TearDown that you created during SetUp. Even though things might be cleaned up later it is good practice and will enable you to look for resource leaks while unit testing.

    Since TMock<T> is a record that internally holds interfaces you need to make sure those are cleaned up after the test run although they might be overridden by the next SetUp or when the testcase instance gets destroyed.

    That is what .Free is for (despite the comment in the source)

    This is even more important once you do more complex things with the mock as it might keep things alive or point to invalid references after the test. I have seen some bad crashes at the end of test applications that did not clean up their mocks.