Основная проблема, как я вижу, заключается в том, что TMock<T>.Create
приводит к созданию экземпляра класса (CUT). Я подозреваю, что структура была разработана в предположении, что вы издеваетесь над абстрактным базовым классом. В этом случае создание экземпляра было бы мягким. Я подозреваю, что вы имеете дело с устаревшим кодом, который не имеет удобного абстрактного базового класса для CUT. Но в вашем случае единственный способ создать экземпляр CUT включает передачу параметров конструктору и, таким образом, побеждает всю цель насмешки. И я скорее представляю, что будет много работы по перепроектированию базы устаревшего кода, пока у вас не будет абстрактного базового класса для всех классов, которые нужно издеваться.
Вы пишете TMock<TFoo>.Create
, где TFoo
- это класс. Это приводит к созданию прокси-объекта.Это происходит в TObjectProxy<T>.Create
. Код, который выглядит следующим образом:
constructor TObjectProxy<T>.Create;
var
ctx : TRttiContext;
rType : TRttiType;
ctor : TRttiMethod;
instance : TValue;
begin
inherited;
ctx := TRttiContext.Create;
rType := ctx.GetType(TypeInfo(T));
if rType = nil then
raise EMockNoRTTIException.Create('No TypeInfo found for T');
ctor := rType.GetMethod('Create');
if ctor = nil then
raise EMockException.Create('Could not find constructor Create on type ' + rType.Name);
instance := ctor.Invoke(rType.AsInstance.MetaclassType, []);
FInstance := instance.AsType<T>();
FVMInterceptor := TVirtualMethodInterceptor.Create(rType.AsInstance.MetaclassType);
FVMInterceptor.Proxify(instance.AsObject);
FVMInterceptor.OnBefore := DoBefore;
end;
Как вы можете видеть, что код делает предположение, что ваш класс имеет конструктор без параметров. Когда вы вызываете это в своем классе, у конструктора которого есть параметры, это приводит к исключению RTTI во время выполнения.
Как я понимаю код, экземпляры класса создаются исключительно с целью перехвата виртуальных методов. Мы не хотим ничего делать с классом, так как это скорее победит цель издевательства над ним. Все, что вам действительно нужно, это экземпляр объекта с подходящей виртуальной таблицей, которую можно манипулировать TVirtualMethodInterceptor
. Вам не нужно или хотите, чтобы ваш конструктор запускался. Вы просто хотите быть в состоянии высмеять класс, который имеет конструктор с параметрами.
Так вместо того, чтобы этот код вызова конструктора Я предлагаю вам изменить его, чтобы сделать его называют NewInstance
. Это минимальный минимум, который вам нужно сделать, чтобы иметь возможность управлять виртуальной таблицей. И вам также потребуется изменить код, чтобы он не пытался уничтожить экземпляр mock и вместо этого набрал FreeInstance
. Все это будет прекрасно работать, пока все, что вы делаете, это вызов виртуальных методов на макет.
Модификации выглядеть следующим образом:
constructor TObjectProxy<T>.Create;
var
ctx : TRttiContext;
rType : TRttiType;
NewInstance : TRttiMethod;
instance : TValue;
begin
inherited;
ctx := TRttiContext.Create;
rType := ctx.GetType(TypeInfo(T));
if rType = nil then
raise EMockNoRTTIException.Create('No TypeInfo found for T');
NewInstance := rType.GetMethod('NewInstance');
if NewInstance = nil then
raise EMockException.Create('Could not find NewInstance method on type ' + rType.Name);
instance := NewInstance.Invoke(rType.AsInstance.MetaclassType, []);
FInstance := instance.AsType<T>();
FVMInterceptor := TVirtualMethodInterceptor.Create(rType.AsInstance.MetaclassType);
FVMInterceptor.Proxify(instance.AsObject);
FVMInterceptor.OnBefore := DoBefore;
end;
destructor TObjectProxy<T>.Destroy;
begin
TObject(Pointer(@FInstance)^).FreeInstance;//always dispose of the instance before the interceptor.
FVMInterceptor.Free;
inherited;
end;
Откровенно это выглядит немного более разумным мне. Разумеется, нет смысла вызывать конструкторы и деструкторы.
Пожалуйста, дайте мне знать, если я знаком с этим знаком и пропустил этот пункт. Это вполне возможно!
Вот что я не понимаю. Если вы пытаетесь издеваться над классом, зачем вам нужен экземпляр класса, который вы издеваетесь над созданием. Наверняка весь смысл насмешек заключается в том, что вы, ну, издеваетесь над классом. –
Когда вы делаете 'TMock. Создайте фреймворк Mocks, создав экземпляр' TFoo'. Возможно, я не понимаю насмешек, но я думал, что все дело в том, что вы создали нечто, что не было 'TFoo'. Я имею в виду, если все, что вам нужно сделать, это создать 'TFoo', тогда просто сделайте это. Если вы хотите издеваться над этим, найдите инфраструктуру, которая создаст макет 'TFoo', а не экземпляр' TFoo'. –
@ Давид. Мне жаль, что мой вопрос не подходит к моей проблеме без каких-либо предпосылок; Ты прав. Я хочу высмеять класс, у конструктора которого есть параметр (ы). Так как образец, представленный в проекте Delphi-Mocks, показывает [образец TesTObjectMock] (https://github.com/VSoftTechnologies/Delphi-Mocks/blob/master/Sample1Main.pas) проверяемый класс (TFoo) передается как общий параметр, как в mock: = TMock .create. Проблема заключается в функции класса «Создать», и она вызывает «Invoke». –
TDF