EInvalidCast使用mock函数返回指针类型

时间:2017-09-26 18:25:50

标签: delphi delphi-xe2 rtti delphi-mocks

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

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

问题是TMock设置界面需要一个TValue对象来指定模拟函数的默认返回值,而我无法从中找到如何正确执行此操作的方法。可用TValue个功能。虽然我已经看到ekPointer是有效的TTypeKind值。

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

我的代码大致类似于 1

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的代码太多了,所以我只是在这里草拟它以给你背景,参见{{1 }}

2 个答案:

答案 0 :(得分:4)

将原始指针指定给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,否则返回{类型为TValue的{​​1}}具有指针的值,即使它实际上并未指向有效的类类型。

如果您将指针传递给TClass,它会返回TValue.From<T>()类型为TValue的{​​{1}},其值为指针,即使它是nil。

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

答案 1 :(得分:1)

  

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

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

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

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

并且有点投射。