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
没有原始指针的隐式转换,但它确实有 TObject
和 TClass
指针的隐式转换。
如果你将一个无类型的 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。
这种差异很微妙,但非常重要。
我编写了一个接口来包装 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 aPTP_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
没有原始指针的隐式转换,但它确实有 TObject
和 TClass
指针的隐式转换。
如果你将一个无类型的 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。
这种差异很微妙,但非常重要。