Question

The class I am doing Unit Testing scrolls each record of a DB table and sum the value in a field to the previous value. The following is the class reduced to the bone:

procedure TSumList.Sum;
var
  FSum:integer;
begin
  FSum:=0;
  FDB.First;
  while not FDB.EOF do
  begin
    FSum:=FSum+FDB.GetAmount;
    FDB.Next;
  end;
end;

FDB refers to the DB mock interface named IIDBTable.

The following is the DB mock for the dependency injection:

  IIDBTable = interface
    ['{A299D1D6-93AF-45CC-8DE2-9A4EE188C352}']
    procedure First;
    procedure Next;
    function EOF : boolean;
    function GetAmount:integer;
  end;

  TMockDBTable = class (TInterfacedObject,IDBTable)
    procedure First;
    procedure Next;
    function EOF : boolean;
    function GetAmount:integer;
  end;

Problem is I don't know how to provide data to the mock for the test. Of course I can add an extra procedure, say AddValues(aAmount:integer), that does the job but in that case I would end up with this extra procedure in production too and I don't need it.

What is the best practice for this ?

I use Spring for Delphi framework

Was it helpful?

Solution 2

Add an extra method to the mock class, AddValues. It will exist only in that mock class, not in the real class, so your objection that the extra code would appear in production is unfounded.

I suspect you thought the code would need to be in production because you're dealing with the mock object exclusively through the interface. The extra method would exist in the interface, and would therefore need to be implemented in the production version of the class, even though it doesn't belong there.

Instead, instantiate the TMockDBTable and access it through the object reference to set it up however it's needed for the test. Once it's ready, then switch to using it via the IIDBTable interface.

Another option is to feed the data into the class through a parameter you add to its constructor. Then you don't even need the extra method, and so there's no temptation to define that method on the interface or on the production class. There's no reason your mock class's constructor needs to look like the production class's constructor since the classes aren't related at all. The constructor isn't part of the interface definition.

OTHER TIPS

You can also use DSharp mocks (or Delphi Mocks).

This will be the setup code for DSharp (Delphi Mocks should be similar)

var
  mockDBTable: Mock<IIDBTable>;
begin
  mockDBTable.Setup.WillExecute.Once.WhenCalling.First;
  mockDBTable.Setup.WillReturn(False).Exactly(5).WhenCalling.EOF;
  mockDBTable.Setup.WillReturn(True).Once.WhenCalling.EOF;
  mockDBTable.Setup.WillReturn(5).Once.WhenCalling.GetAmount;
  mockDBTable.Setup.WillReturn(4).Once.WhenCalling.GetAmount;
  mockDBTable.Setup.WillReturn(3).Once.WhenCalling.GetAmount;
  mockDBTable.Setup.WillReturn(2).Once.WhenCalling.GetAmount;
  mockDBTable.Setup.WillReturn(1).Once.WhenCalling.GetAmount;
  mockDBTable.Setup.WillExecute.Exactly(5).WhenCalling.Next;

What you do here is specify what you expect to be called and what to be returned. This saves you from manually writing a mock class and feed it with data.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top