正如我所看到的,根本問題是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異常。
據我瞭解的代碼,類實例化僅用於截取其虛擬方法的目的。我們不想在課堂上做任何其他事情,因爲那樣會打敗嘲笑它的目的。您真正需要的是具有合適的vtable的對象的實例,可由TVirtualMethodInterceptor
操縱。你不需要或者不想讓你的構造函數運行。你只是希望能夠模擬恰好具有參數的構造函數的類。
因此,而不是調用構造函數的代碼,我建議您修改它以使其調用NewInstance
。這是爲了擁有一個可以被操縱的vtable所需要做的最低限度的事情。而且您還需要修改代碼,以便它不會試圖銷燬模擬實例,而是調用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;
坦率地說,這看起來更明智一點給我。調用構造函數和析構函數肯定沒有意義。
請讓我知道,如果我在這裏廣泛的標誌,並錯過了這一點。這完全有可能!
這是我不明白。如果你想模擬一個類,爲什麼你想要創建一個你正在嘲笑的類的實例。當然,嘲笑的全部意義在於,你嘲笑課堂。 – 2013-03-23 14:18:46
當您執行'TMock。創建'Mocks框架將創建'TFoo'的實例。也許我不明白嘲笑,但我認爲整個觀點是你創造了一些不是'TFoo'的東西。我的意思是,如果你需要做的就是創建'TFoo',那麼就做吧。如果你想嘲笑它,然後找到一個框架,將創建一個'TFoo'的模擬,而不是'TFoo'的一個實例。 –
2013-03-23 14:47:09
@David。我很抱歉,我的問題跳到我的問題沒有任何背景;你是對的。我想模擬一個構造函數有一個參數的類。正如在Delphi-Mocks項目展示[TesTObjectMock示例](https://github.com/VSoftTechnologies/Delphi-Mocks/blob/master/Sample1Main)上提供的示例一樣。pas)被測試的類(TFoo)作爲通用參數傳遞,如同mock:= TMock .create。問題在於類函數「Create」,它調用「Invoke」。 –
TDF
2013-03-23 14:53:52