我编写了一个包装Windows Threadpool API的接口,其中许多函数返回普通的Pointer类型。

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

问题在于TMock设置界面使用TValue对象为模拟函数指定默认返回值,而我无法从可用的TValue函数中看到如何正确执行此操作的方法。尽管我已经看到ekPointer是有效的TTypeKind值。

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

我的代码大致如下所示:

type
    PTP_POOL = Pointer;
    PTP_CLEANUP_GROUP = Pointer;

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


被测课程

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 ****}

最佳答案

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

TValue.From<PTP_POOL>(nil);


或者,如果要返回文字值:

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


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

如果直接将无类型的nil指针分配给TValue,它将调用TValue.Implicit(TObject),当传递nil指针时,它将返回TValue.Empty属性。

如果将类型化的非TObject指针直接分配给TValue,它将调用TValue.Implicit(TClass),如果指针为nil,则返回TValue.Empty,否则返回值为TValueTClass类型指针,即使它实际上没有指向有效的类类型也是如此。

如果将指针传递给TValue.From<T>(),即使它为nil,它也会返回带有指针值的TValue类型的T

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

关于delphi - EInvalidCast带模拟函数返回指针类型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46433385/

10-15 05:27