シールされたクラスをモックすることは、非常に面倒です。私は現在、これを処理するためにAdapter パターンを好みますが、単に維持することについては何かが奇妙に感じます。
では、封印されたクラスをモックする最良の方法は何ですか?
Java の回答は大歓迎です。実際、Java コミュニティはこれに長く取り組んでおり、多くのことを提供していると思います。
ただし、.NET に関する意見の一部を次に示します。
シールされたクラスをモックすることは、非常に面倒です。私は現在、これを処理するためにAdapter パターンを好みますが、単に維持することについては何かが奇妙に感じます。
では、封印されたクラスをモックする最良の方法は何ですか?
Java の回答は大歓迎です。実際、Java コミュニティはこれに長く取り組んでおり、多くのことを提供していると思います。
ただし、.NET に関する意見の一部を次に示します。
.NET の場合、TypeMockのようなものを使用できます。これは、プロファイリング API を使用し、ほぼすべての呼び出しにフックできるようにします。
私の一般的な経験則では、モックする必要のあるオブジェクトにも共通のインターフェースが必要です。これは設計的には正しく、テストがはるかに簡単になると思います(通常、TDDを実行すると得られるものです)。これについての詳細は、Google Testing Blogの最新の投稿で読むことができます(ポイント9を参照)。
また、私は過去4年間、主にJavaで作業しており、一方で、最終的な(封印された)クラスを作成した回数を数えることができると言えます。ここでのもう1つのルールは、デフォルトでクラスを封印するのではなく、常にクラスを封印する正当な理由があるはずです。
Microsoft Research の Moles によって、それが可能になると思います。モグラのページから:
モールは、シールされた型の非仮想/静的メソッドを含む、任意の .NET メソッドを迂回するために使用できます。
更新: Moles を置き換えるように設計された次の VS 11 リリースには、「Fakes」と呼ばれる新しいフレームワークがあります。
Visual Studio 11のFakes Framework は次世代の Moles & Stubs であり、最終的にはこれに取って代わります。ただし、Fakes は Moles とは異なるため、Moles から Fakes に移行するにはコードを変更する必要があります。この移行のガイドは後日公開されます。
要件: Visual Studio 11 Ultimate、.NET 4.5
TypeMock の問題は、悪い設計を言い訳にすることです。他人の悪い設計が隠されていることが多いことはわかっていますが、開発プロセスにそれを許可すると、自分の悪い設計を簡単に許可してしまう可能性があります。
モッキング フレームワークを使用する場合は、従来のフレームワーク (Moq など) を使用し、モックできないものの周りに分離レイヤーを作成し、代わりに分離レイヤーをモックする必要があると思います。
私はほとんどの場合、コードの奥深くで外部クラスに依存することを避けています。代わりに、私はむしろアダプター/ブリッジを使用して彼らと話をしたいと思います。そうすれば、私は自分のセマンティクスを扱っており、翻訳の苦痛は1つのクラスに分離されています。
また、長期的には依存関係を簡単に切り替えることができます。
私は最近この問題に遭遇し、ウェブを読んだり検索したりした後、上記の別のツールを使用する以外に簡単な方法はないようです. または、私がしたように物事を処理するのは粗雑です:
System.Runtime.Serialization.FormatterServices.GetUninitializedObject(instanceType);
リフレクションを介してプロパティ/フィールドに値を割り当てます
私は通常、封印されたタイプのモックを容易にするために、インターフェースとアダプター/プロキシクラスを作成するルートを取ります。ただし、インターフェイスの作成をスキップし、仮想メソッドを使用してプロキシタイプを非封印にすることも実験しました。これは、プロキシが実際にシールされたクラスの一部をカプセル化してユーザーにする自然な基本クラスである場合にうまく機能しました。
この適応を必要とするコードを扱うとき、私はインターフェイスとプロキシタイプを作成するために同じアクションを実行することにうんざりしていたので、タスクを自動化するためにライブラリを実装しました。
コードは、(ソースコードの代わりに)アセンブリを生成し、任意のタイプでコード生成を実行でき、多くの構成を必要としないため、参照する記事に記載されているサンプルよりもいくらか洗練されています。
詳細については、このページを参照してください。
多くのフレームワーク クラスは封印されているため、封印されたクラスをモックすることは完全に合理的です。
私の場合、.Net の MessageQueue クラスをモックして、適切な例外処理ロジックを TDD できるようにしようとしています。
「オーバーライドできないメンバーの無効なセットアップ」に関するMoqのエラーを克服する方法について誰かがアイデアを持っている場合は、私に知らせてください。
コード:
[TestMethod]
public void Test()
{
Queue<Message> messages = new Queue<Message>();
Action<Message> sendDelegate = msg => messages.Enqueue(msg);
Func<TimeSpan, MessageQueueTransaction, Message> receiveDelegate =
(v1, v2) =>
{
throw new Exception("Test Exception to simulate a failed queue read.");
};
MessageQueue mockQueue = QueueMonitorHelper.MockQueue(sendDelegate, receiveDelegate).Object;
}
public static Mock<MessageQueue> MockQueue
(Action<Message> sendDelegate, Func<TimeSpan, MessageQueueTransaction, Message> receiveDelegate)
{
Mock<MessageQueue> mockQueue = new Mock<MessageQueue>(MockBehavior.Strict);
Expression<Action<MessageQueue>> sendMock = (msmq) => msmq.Send(It.IsAny<Message>()); //message => messages.Enqueue(message);
mockQueue.Setup(sendMock).Callback<Message>(sendDelegate);
Expression<Func<MessageQueue, Message>> receiveMock = (msmq) => msmq.Receive(It.IsAny<TimeSpan>(), It.IsAny<MessageQueueTransaction>());
mockQueue.Setup(receiveMock).Returns<TimeSpan, MessageQueueTransaction>(receiveDelegate);
return mockQueue;
}
現在はベータ リリースでしか利用できませんが、新しいFakes フレームワーク( Visual Studio 11ベータ リリースの一部) のshim機能を覚えておく価値があると思います。
Shim 型は、任意の .NET メソッドをユーザー定義のデリゲートに迂回させるメカニズムを提供します。Shim 型は Fakes ジェネレーターによってコード生成され、shim 型と呼ばれるデリゲートを使用して新しいメソッドの実装を指定します。内部では、shim 型は、実行時にメソッド MSIL 本体に挿入されたコールバックを使用します。
個人的には、これを使用して、DrawingContext などのシールされたフレームワーク クラスのメソッドをモックすることを考えていました。
インターフェイスからシールされたクラスを実装する方法はありますか...代わりにインターフェイスをモックしますか?
そもそも封印されたクラスを持つことは間違っていると私の中の何かが感じますが、それは私だけです:)