Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Как перехватить функцию UuidCreate в Delphi для целей модульного тестирования, если её объявление скрыто в System.SysUtils?

Delphi , Компоненты и Классы , Процедуры и Функции

 

В мире разработки программного обеспечения модульное тестирование играет критически важную роль в обеспечении качества и надежности кода. Однако, иногда возникают ситуации, когда необходимо протестировать код, зависящий от внешних функций, таких как UuidCreate, объявление которой может быть скрыто в системных модулях, таких как System.SysUtils в Delphi. В этой статье мы рассмотрим различные подходы к решению этой проблемы, опираясь на обсуждение, возникшее на форуме Delphi.

Проблема:

Необходимо перехватить функцию UuidCreate для целей модульного тестирования, используя библиотеку DDetours. Проблема заключается в том, что объявление этой функции недоступно напрямую, так как оно находится в секции implementation модуля System.SysUtils.

Решение 1: Прямое объявление функции

Remy Lebeau предлагает простое и эффективное решение: объявить функцию UuidCreate в собственном коде. Поскольку UuidCreate является функцией Win32 API, находящейся в rpcrt4.dll, достаточно создать собственное объявление, чтобы получить к ней доступ для перехвата.

function UuidCreate(out guid: TGUID): Longint; stdcall; external 'rpcrt4.dll' name 'UuidCreate';

Это объявление позволяет использовать функцию InterceptCreate из DDetours для перехвата вызовов UuidCreate. Важно помнить, что физически существует только одна функция в rpcrt4.dll, поэтому не имеет значения, сколько объявлений существует для доступа к ней.

Пример использования DDetours (на основе примера dwrbudr):

function Detour_UuidCreate(out guid: TGUID): Longint; stdcall;
begin
  guid := Default(TGUID); // Или любое другое предопределенное значение
  Result := 0; // Или код ошибки, если необходимо
end;

procedure TForm68.Button1Click(Sender: TObject);
var
  myguid: TGUID;
begin
  InterceptCreate('rpcrt4.dll', 'UuidCreate', @Detour_UuidCreate);
  CreateGUID(myguid); // Вызов функции, которая использует UuidCreate
  // ...
end;

Альтернативное решение 2: Абстракции и Mock-объекты

Vincent Parrett предлагает более элегантный и рекомендуемый подход: использование абстракций и Mock-объектов. Вместо прямого перехвата функции UuidCreate, создается абстракция (интерфейс) для генерации UUID, и конкретная реализация, которая вызывает UuidCreate. Для целей тестирования можно использовать Mock-объекты, которые возвращают предопределенные значения, что позволяет изолировать тестируемый код от внешних зависимостей.

interface

  IUuidGenerator = interface
    ['{...}'] // GUID интерфейса
    function Generate: TGUID;
  end;

  TRealUuidGenerator = class(TInterfacedObject, IUuidGenerator)
  public
    function Generate: TGUID;
  end;

implementation

uses
  System.SysUtils; // Или прямое объявление UuidCreate, как описано выше

function TRealUuidGenerator.Generate: TGUID;
var
  ResultCode: Longint;
begin
  ResultCode := UuidCreate(Result);
  if ResultCode <> 0 then
  begin
    // Обработка ошибки
    raise Exception.Create('Ошибка при создании UUID: ' + IntToStr(ResultCode));
  end;
end;

// В модуле тестирования:

type
  TMockUuidGenerator = class(TInterfacedObject, IUuidGenerator)
  private
    FReturnValue: TGUID;
  public
    constructor Create(const AReturnValue: TGUID);
    function Generate: TGUID; override;
  end;

constructor TMockUuidGenerator.Create(const AReturnValue: TGUID);
begin
  inherited Create;
  FReturnValue := AReturnValue;
end;

function TMockUuidGenerator.Generate: TGUID;
begin
  Result := FReturnValue;
end;

// Пример использования в тестируемом коде:

type
  TMyClass = class
  private
    FUuidGenerator: IUuidGenerator;
  public
    constructor Create(AUuidGenerator: IUuidGenerator);
    function DoSomething: string;
  end;

constructor TMyClass.Create(AUuidGenerator: IUuidGenerator);
begin
  inherited Create;
  FUuidGenerator := AUuidGenerator;
end;

function TMyClass.DoSomething: string;
var
  NewGuid: TGUID;
begin
  NewGuid := FUuidGenerator.Generate;
  Result := GUIDToString(NewGuid); // Или другая логика с использованием GUID
end;

// Пример использования в тесте:

procedure TestMyClass;
var
  MockGenerator: TMockUuidGenerator;
  MyClass: TMyClass;
  ExpectedGuid: TGUID;
  ActualResult: string;
begin
  // Arrange
  ExpectedGuid := StringToGUID('{12345678-1234-1234-1234-1234567890AB}');
  MockGenerator := TMockUuidGenerator.Create(ExpectedGuid);
  MyClass := TMyClass.Create(MockGenerator);

  // Act
  ActualResult := MyClass.DoSomething;

  // Assert
  Assert.AreEqual(GUIDToString(ExpectedGuid), ActualResult);
end;

Этот подход позволяет легко подменять реализацию UuidGenerator в тестах, используя Mock-объекты, такие как TMockUuidGenerator. Можно использовать фреймворки для Mock-объектов, такие как Delphi Mocks или Spring4D (хотя EugeneK отмечает, что пока не использует Spring4D из-за отсутствия поддержки пространств имен).

Преимущества использования абстракций и Mock-объектов:

  • Изоляция: Тестируемый код изолирован от внешних зависимостей, что делает тесты более надежными и предсказуемыми.
  • Гибкость: Легко подменять реализации для различных тестовых сценариев.
  • Читаемость: Код становится более читаемым и понятным.
  • Поддержка TDD: Подходит для разработки через тестирование (TDD).

Альтернативное решение 3: Использование предопределенной GUID для тестирования

Artem Razin предлагает простое решение для целей тестирования: использовать кастомную реализацию, которая всегда возвращает предопределенный GUID. Это может быть полезно, если важна только проверка корректности обработки GUID, а не уникальность.

Вывод:

В зависимости от конкретных требований и предпочтений, можно выбрать один из предложенных подходов для перехвата функции UuidCreate в Delphi для целей модульного тестирования. Прямое объявление функции с использованием DDetours является простым и быстрым решением. Однако, использование абстракций и Mock-объектов является более элегантным и рекомендуемым подходом, обеспечивающим лучшую изоляцию, гибкость и читаемость кода. Использование предопределенной GUID - самое простое решение, если не требуется уникальность GUID в тестах.

Создано по материалам из источника по ссылке.

В статье рассматриваются различные подходы к перехвату функции `UuidCreate` в Delphi для целей модульного тестирования, включая прямое объявление функции, использование абстракций и Mock-объектов, а также использование предопределенной GUID.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Процедуры и Функции ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-09-29 22:53:19/0.0066099166870117/0