Search code examples
delphispring4d

How to mock a function that has constant array and anonymous function as parameters


I have an interface function that has a constant array and an anonymous function as parameters:

  TCodeword = array[0..7] of Char;
  TIntFunc = reference to function: Integer;

  IMyInterface = interface(IInvokable)
    function DoSomething(const codeword: TCodeword; func: TIntFunc): Boolean;
  end;

I want to mock that interface to test an object, that is using it:

  function IntFunc: Integer;
  begin
    Result := 5;
  end;  
     
  procedure Test;
  var
    MyInterfaceMock: Mock<IMyInterface>; 
    MyInterface: IMyInterface;
  begin
    MyInterfaceMock := Mock<IMyInterface>.Create(TMockbehavior.Strict);
    MyInterfaceMock.Setup.Returns(true).When.DoSomething(arg.IsAny<TCodeword>, arg.IsAny<TIntFunc>());
    
    MyInterface := MyInterfaceMock;
    MyInterface.DoSomething('12345678', IntFunc); 
  end;

When running, an ENotSupportedException: ‚Type is not supported: TCodeword‘ is raised when Setup. Can somebody explain why this is a not supported type? How can I pass a not specified TCodeword to mock that function correctly?

Alternatively I tried to pass explicit arguments in Setup:

  procedure Test;
  var
    MyInterfaceMock: Mock<IMyInterface>; 
    MyInterface: IMyInterface;
  begin
    MyInterfaceMock := Mock<IMyInterface>.Create(TMockbehavior.Strict);
    MyInterfaceMock.Setup.Returns(true).When.DoSomething('12345678', IntFunc);
    
    MyInterface := MyInterfaceMock;
    MyInterface.DoSomething('12345678', IntFunc); 
  end;

That way it will work for the constant array but not for the anonymous function. I get an EMockException: 'unexpected call of function DoSomething(const codeword: TCodeword; func: TIntFunc): Boolean with arguments: nil, (array)';

How can I make this work? I am glad for any help!


Solution

  • There are multiple issues:

    1. as the exception states the TCodeword type is not supported - that is because types of typeKind tkArray are not supported - I don't remember exactly why that is because internally the handling is very similar to tkDynArray. I will fix that and put an edit to this answer once done.

    2. when passing a regular function to a method reference parameter the compiler builds the necessary code to wrap the regular function into a method reference and it uses an interfaced object for that which implements the interface of the method reference (after all anonymous methods are just interfaces). It however does that for every time this happens which means that two lines that pass IntFunc to an TIntFunc parameter are two different pointers. That is why internally the parameter matcher returns False. If you want to avoid that you need to put IntFunc into a local variable of type TIntFunc and pass that. Because then the compiler only builds that wrapping code once and in both cases the value of the local variable gets passed to the DoSomething call.

    Update: fixed in develop branch