В статье будет рассмотрен вопрос корректного создания объектов TValue для работы с указателями, возвращаемыми функциями Windows Threadpool API, при тестировании с использованием фреймворка Delphi-Mocks. В процессе решения проблемы будет использован язык Object Pascal, а также приведены примеры кода.
Оригинальный заголовок:
EInvalidCast wih mock function returning a pointer type
Введение
При работе с Windows Threadpool API многие функции возвращают указатели. При тестировании кода, обёртывающего данный API, возникает необходимость использования фреймворка Delphi-Mocks для создания моков. Однако, при попытке установить значения по умолчанию для мок-методов, возвращающих указатели, возникает исключение EInvalidCast. Это связано с тем, что интерфейс настройки моков TMock ожидает объект TValue для указания значения по умолчанию, которое должно быть корректно инициализировано.
Проблема
Разработчик столкнулся с проблемой при работе с моками функций, возвращающих указатели в интерфейсе IThreadPoolApi. При попытке установить значение по умолчанию для мок-метода CreateThreadPool с помощью метода WillReturnDefault возникло исключение EInvalidCast, так как TValue не был инициализирован корректно для хранения указателя типа PTP_POOL.
Решение
Для корректного создания TValue, содержащего указатель, необходимо использовать метод TValue.From<T>(). В данном случае, для указателя типа PTP_POOL, следует использовать:
Также, при использовании метода WillExecute, необходимо возвращать TValue с использованием аналогичного подхода:
apiMock.Setup.WillExecute('CreateThreadpoolCleanupGroup',
function (const args: TArray<TValue>; const ReturnType: TRttiType): TValue
begin
Result := TValue.From<PTP_CLEANUP_GROUP>(nil);
end);
Важные замечания
При работе с TValue важно понимать различия в инициализации объектов для различных типов указателей. Например, при прямом присвоении нетипизированного nil-указателя TValue будет вызван метод TValue.Implicit(TObject), который вернёт свойство TValue.Empty. При использовании типизированного указателя, не являющегося указателем на TObject, будет вызван метод TValue.Implicit(TClass), который также вернёт TValue.Empty для nil-указателя, но вернёт TValue типа TClass с значением указателя в случае, если он не равен nil.
Заключение
Использование метода TValue.From<T>() позволяет корректно инициализировать TValue для хранения указателей, что является ключевым моментом при работе с моками в Delphi-Mocks. Приведённые примеры кода демонстрируют, как правильно настроить моки для функций, возвращающих указатели.
Пример кода
type
PTP_POOL = Pointer;
PTP_CLEANUP_GROUP = Pointer;
IThreadPoolApi = interface(IInterface)
function CreateThreadPool(): PTP_POOL;
function CreateThreadpoolCleanupGroup(): PTP_CLEANUP_GROUP;
end;
TThreadPool = class(TInterfacedObject, IThreadPool)
private
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;
FPTPPool := FTPApi.CreateThreadPool;
// Проверка на не nullptr и т.д.
FPTPCleanupGroup := FTPApi.CreateThreadpoolCleanupGroup;
// Проверка на не nullptr и т.д.
end;
procedure ThreadPoolTest.TestFail_CreateThreadpoolCleanupGroup;
var
apiMock: TMock<IThreadPoolApi>;
testproc: TTestLocalMethod;
begin
apiMock := TMock<IThreadPoolApi>.Create;
apiMock.Setup.WillReturnDefault('CreateThreadPool', TValue.From<PTP_POOL>(PTP_POOL($FFFFFFFF)));
apiMock.Setup.WillExecute('CreateThreadpoolCleanupGroup',
function
(const args: TArray<TValue>; const ReturnType: TRttiType): TValue
begin
Result := TValue.From<PTP_CLEANUP_GROUP>(nil);
end);
testproc := procedure
var
threadpool: IThreadPool;
begin
threadpool := TThreadPool.Create(apiMock);
end;
DUnitX.Assert.WillRaise(testproc, EThreadPoolError, 'Cannot create TP cleanup group');
end;
Итог
При правильном использовании метода TValue.From<T>() можно избежать исключения EInvalidCast и корректно настроить моки функций, возвращающих указатели, в Delphi-Mocks.
Статья посвящена корректному созданию объектов `TValue` для работы с указателями в Windows Threadpool API и использованию фреймворка Delphi-Mocks для тестирования, с акцентом на решение проблемы с исключением `EInvalidCast`.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.