基本的な問題は、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;
率直に言って、これは私にはもう少し賢明に見えます。コンストラクタとデストラクタを呼び出す意味はありません。
私がここでマークから外れていて、要点を逃した場合はお知らせください。それは完全に可能です!