我编写了一个包装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 }} 子>
答案 0 :(得分:4)
将原始指针指定给TValue
时,请使用TValue.From<T>()
方法,例如:
TValue.From<PTP_POOL>(nil);
或者,如果要返回文字值:
TValue.From<PTP_POOL>(PTP_POOL(value));
TValue
没有针对原始指针的隐式转换,但它确实具有TObject
和TClass
指针的隐式转换。
如果将无类型的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)));
并且有点投射。