Search code examples
delphispring4ddunitx

How can I mock a method call with var parameter in DUnitX and Spring4D 1.2.2


How can I mock an interface method call like procedure foo( var i_ : integer ). The tested method local variable passed as a var param, so the test must use Arg.IsAny (The test does not access it). The result value is not the same as the out value of the var param, because the tested method does some processing on it before gives back as a result. The commented When variations in the test does not compile. The current one compiles but results an undefined value (mock Executes does not call at all, because the var=pointer values don't match). How could I mock a method call with a var parameter?

unit Unit1;

interface

uses
    DUnitX.TestFramework
  , Spring.Mocking
  ;

type
  IMyInterface = interface ( IInvokable )
    ['{606BA1D8-EAEC-42CB-A774-911628FD2E6C}']
    procedure foo( var x_ : integer );
  end;

  TMyClass = class
    private
      fMyInterface : IMyInterface;
    public
      constructor Create( myInterface_ : IMyInterface );
      function bar : integer;
  end;

  [TestFixture]
  TMyClassUnitTest = class
    public
      [Test]
      procedure bar;
  end;

implementation

constructor TMyClass.Create( myInterface_ : IMyInterface );
begin
  inherited Create;
  fMyInterface := myInterface_;
end;

function TMyClass.bar : integer;
var
  i : integer;
begin
  fMyInterface.foo( i );
  result := i + 1;
end;

procedure TMyClassUnitTest.bar;
var
  myInterfaceMock : Mock<IMyInterface>;
  myClass : TMyClass;
  i : integer;

  procedure prepareMyInterfaceFooCall( fooVarValue_ : integer );
  var
    ii : integer;
  begin
    ii := 7;
    myInterfaceMock.Setup.Executes(

      function ( const args_ : TCallInfo ) : TValue
      begin
        args_[0] := TValue.From<integer>( fooVarValue_ );
      end

    //).When.foo( Arg.IsAny<integer> );
    //).When.foo( integer( Arg.IsAny<integer> ) );
    ).When.foo( ii );
  end;

begin
  prepareMyInterfaceFooCall( 5 );
  myClass := TMyClass.Create( myInterfaceMock );
  try
    i := myClass.bar;
  finally
    FreeAndNIL( myClass );
  end;
  Assert.AreEqual( 6, i );
end;

end.

Solution

  • 1.2.2 cannot do this but 2.0 can do (currently develop branch)

    Here is the relevant change to your code:

    procedure prepareMyInterfaceFooCall(expectedValue: Integer);
    begin
      myInterfaceMock.Setup.Executes
      // put the wildcard matcher because your code passes a non initialized variable
      // a matcher on the When() always has priority over any individual parameter matching
      .When(Args.Any)
      // use the Arg.Ref syntax specifying the return value
      .foo(Arg.Ref<Integer>(expectedValue).Return);
    end;