EInvalidCast 带有返回指针类型的模拟函数

EInvalidCast wih mock function returning a pointer type

我编写了一个接口来包装 Windows 线程池 API 和其中许多函数 return 普通 Pointer 类型。

现在我正在编写测试并想使用 delphi-mocks framework 来模拟包装器接口。

问题是 TMock 设置界面采用 TValue 对象来为模拟函数指定默认 return 值,我看不出如何做从可用的 TValue functions. Though I've seen that ekPointer is a valid TTypeKind 值中正确。

调用模拟函数时,我从相应的 RTTI 调用中收到 EInvalidCast 异常。
当 RTTI 调用尝试从隐式 TValue 对象转换 return 类型值时,就会发生这种情况。

我的代码大致如下1:

type
    PTP_POOL = Pointer;
    PTP_CLEANUP_GROUP = Pointer;

    IThreadPoolApi = interface(IInterface)
        {...}
        function CreateThreadPool() : PTP_POOL;
        function CreateThreadpoolCleanupGroup() : PTP_CLEANUP_GROUP;
        {...}
    end;

正在测试class

type
    TThreadPool = class(TInterfacedObject, IThreadPool)
        FTPApi : IThreadPoolApi;
        FPTPPool : PTP_POOL;
        FPTPCleanupGroup : PTP_CLEANUP_GROUP;
        {...}
    public
        constructor Create(iapi : IThreadPoolApi);
    end;

implementation
constructor TThreadPool.Create(iapi : IThreadPoolApi);
begin
    inherited Create();
    FTPApi := iapi;

    {**** Here I get a EInvalidCast exception when mocking the interface ****}
    FPTPPool := FTPApi.CreateThreadPool();
    if(not assigned(FPTPPool)) then
    begin
        {Raise exception}
        raise EThreadPoolError('Cannot create TP thread pool');
    end;

    {**** This case should be tested ****}
    FPTPCleanupGroup := FTPApi.CreateThreadpoolCleanupGroup();
    if(not assigned(FPTPPool)) then
    begin
         {Raise exception}
         raise EThreadPoolError('Cannot create TP cleanup group');
    end;

    {...}
end;

和测试内容

procedure ThreadPoolTest.TestFail_CreateThreadpoolCleanupGroup();
var
   apiMock : TMock<IThreadPoolApi>;
   testproc : TTestLocalMethod;
begin
   apiMock := TMock<IThreadPoolApi>Create();
   {**** Needed to reach the call of CreateThreadpoolCleanupGroup
    but EInvalidCast is raised ****}
   apiMock.Setup.WillReturnDefault('CreateThreadPool',PTP_POOL($FFFFFFFF));
   {**** The case to be tested ****}
   apiMock.Setup.WillExecute('CreateThreadpoolCleanupGroup',
       function (const args : TArray<TValue>; const ReturnType : TRttiType) 
       : TValue
       begin
           result := nil;
       end);

    testproc := 
       procedure() 
       var
           threadpool : IThreadPool;
       begin
           threadpool := TThreadPool.Create(apiMock);
       end;
    DUnitX.Assert.WillRaise(testproc,EThreadPoolError,
                            'Cannot create TP cleanup group');
end;

TL;DR;

所以问题是:
我需要做什么才能正确创建 TValue 以包含 PTP_POOL 指针类型?


1)设置一个MCVE的代码有点多,所以我在这里画个草图给你背景,看{**** highlighted comments ****}

What do I need to do that TValue is created properly to contain a PTP_POOL pointer type?

看起来@Remy 已经在 中回答了我的问题。

要将该指针值包含在 TValue 实例中,必须使用通用 TValue.From<T>() 函数创建它:

apiMock.Setup.WillReturnDefault('CreateThreadPool',
                                TValue.From<PTP_POOL>(PTP_POOL($FFFFFFFF)));

还有一些铸造。

将原始指针分配给TValue时,使用TValue.From<T>()方法,例如:

TValue.From<PTP_POOL>(nil);

或者,如果您想要 return 文字值:

TValue.From<PTP_POOL>(PTP_POOL(value));

TValue 没有原始指针的隐式转换,但它确实有 TObjectTClass 指针的隐式转换。

如果你将一个无类型的 nil 指针直接赋值给 TValue,它会调用 TValue.Implicit(TObject),当传递一个 nil 时,它将 return TValue.Empty 属性指针。

如果您将类型化的非 TObject 指针直接分配给 TValue,它会调用 TValue.Implicit(TClass),如果指针是 returns TValue.Empty零,否则它 return 是一个 TClass 类型的 TValue 和指针的值,即使它实际上没有指向有效的 class 类型。

如果你传递一个指向 TValue.From<T>() 的指针,它 return 是一个 T 类型的 TValue 和指针的值,即使它是 nil。

这种差异很微妙,但非常重要。