Sealed クラスをどのようにモックしますか?
-
08-06-2019 - |
質問
シールされたクラスのモック化 かなりの苦痛になる可能性があります。私は現在、 アダプターパターン これに対処する必要がありますが、このままでは何か奇妙に感じます。
では、シールされたクラスを模擬する最良の方法は何でしょうか?
Java の回答は大歓迎です. 。実際、Java コミュニティは以前からこの問題に取り組んできており、多くのことを提供できると予想しています。
ただし、.NET の意見の一部を次に示します。
解決
私の一般的な経験則は、モックする必要があるオブジェクトにも共通のインターフェイスが必要であるということです。これは設計的には正しいと思いますし、テストがはるかに簡単になります (TDD を実行すると、通常はこれが得られます)。これについて詳しくは、Google Testing Blog をご覧ください。 最新の投稿 (ポイント9を参照)。
また、私は過去 4 年間、主に Java で作業してきましたが、最終的な (シールされた) クラスを作成した回数は片手で数えられるほどです。ここでのもう 1 つのルールは、クラスをデフォルトでシールするのではなく、クラスをシールする正当な理由を常に持つ必要があるということです。
他のヒント
.NET の場合、次のようなものを使用できます。 タイプモック, 、プロファイリング API を使用し、ほぼあらゆるものへの呼び出しにフックできるようにします。
私はそれを信じています ほくろ, Microsoft Research の を使用すると、それが可能になります。モールのページから:
モルは、密閉型の非仮想/静的メソッドを含む.NETメソッドを迂回するために使用できます。
アップデート: 今後の VS 11 リリースには、Moles を置き換えるように設計された「Fakes」と呼ばれる新しいフレームワークがあります。
の Visual Studio 11 の Fakes フレームワーク は次世代の Moles & Stubs であり、最終的にはそれに置き換わることになります。ただし、Fakes は Moles とは異なるため、Moles から Fakes に移行するにはコードをいくつか変更する必要があります。この移行のガイドは後日公開される予定です。
要件:Visual Studio 11 Ultimate、.NET 4.5
TypeMock の問題は、悪いデザインを許してしまうことです。今、私はそれがよくあることを知っています 他の人の それは悪い設計を隠していますが、それを開発プロセスに許可すると、あなた自身の悪い設計を許可することに非常に簡単につながる可能性があります。
モック フレームワークを使用する場合は、従来のフレームワーク (Moq など) を使用し、モック不可能なものの周囲に分離レイヤーを作成し、代わりに分離レイヤーをモックする必要があると思います。
私はほとんどの場合、コードの奥深くにある外部クラスへの依存関係を避けています。代わりに、アダプタ/ブリッジを使用して通信したいと考えています。そうすることで、意味論に対処できるようになり、翻訳の苦痛が 1 つのクラスに分離されます。
また、長期的には依存関係を切り替えるのも簡単になります。
最近この問題に遭遇し、Web を読んだり検索したりしたところ、上記のように別のツールを使用する以外に簡単な方法はないようです。または、私がやったように物事を扱うのは粗雑です:
- コンストラクターを呼び出さずにシールされたクラスのインスタンスを作成します。
System.Runtime.Serialization.FormatterServices.GetUninitializedObject(instanceType);
リフレクション経由でプロパティ/フィールドに値を割り当てる
- YourObject.GetType().GetProperty("プロパティ名").SetValue(dto, newValue, null);
- YourObject.GetType().GetField("フィールド名").SetValue(dto, newValue);
私は通常、シールされた型のモックを容易にするために、インターフェイスとアダプター/プロキシ クラスを作成するというルートを採用します。ただし、インターフェイスの作成をスキップし、仮想メソッドを使用してプロキシ タイプを非シールにすることも実験しました。これは、プロキシが実際にカプセル化する自然な基本クラスであり、ユーザーがシールされたクラスの一部である場合にうまく機能しました。
この調整が必要なコードを扱うとき、インターフェイスとプロキシ タイプを作成するために同じアクションを実行するのにうんざりしたため、タスクを自動化するライブラリを実装しました。
このコードは、(ソース コードではなく) アセンブリを生成し、任意の型でコード生成を実行できるようにし、多くの構成を必要としないため、参照している記事に記載されているサンプルよりもいくらか洗練されています。
詳細については、を参照してください。 このページ.
多くのフレームワーク クラスはシールされているため、シールされたクラスをモックすることは完全に合理的です。
私の場合、.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;
}
現在はベータ版でのみ利用可能ですが、次の点に留意する価値があると思います。 シム 新しい機能 フェイクフレームワーク (の一部 Visual Studio 11 ベータ版リリース)。
Shim タイプは、.NET メソッドをユーザー定義のデリゲートに迂回するメカニズムを提供します。Shim タイプは Fakes ジェネレーターによってコード生成され、シム タイプと呼ばれるデリゲートを使用して新しいメソッド実装を指定します。内部では、shim タイプは実行時にメソッド MSIL 本体に挿入されたコールバックを使用します。
個人的には、これを使用して、DrawingContext などのシールされたフレームワーク クラスのメソッドをモックすることを検討していました。
インターフェースからシールされたクラスを実装する方法はありますか...代わりにインターフェイスをモックしますか?
私の心のどこかで、そもそもクラスをシールすることが間違っていると感じていますが、それは私だけです:)