私は最近、あざけることについて同僚と話し合いました。彼は、クラスをあざけることは非常に悪いことであり、ほんのわずかな場合にのみ行われるべきではないと述べました。
彼は、インターフェースだけがモック化されるべきだと言っています。そうでなければ、それはアーキテクチャーの誤りです。
なぜこの発言(私は彼を完全に信頼している)がそんなに正しいのだろう?知らないので納得したいです。
私はあざけるポイントを見逃しましたか(はい、読みました Martin Fowlerの記事 )
モッキングはprotocolテストに使用されます-これは、APIの使用方法と、APIがそれに応じて反応するときの反応をテストします。
理想的には(少なくとも多くの場合)、そのAPIはクラスではなくインターフェースとして指定する必要があります。インターフェースはプロトコルを定義し、クラスは実装の少なくとも一部を定義します。
実際には、フレームワークのモックには、クラスのモックに関して制限がある傾向があります。
私の経験では、モッキングはやや使いすぎです-多くの場合、正確な相互作用にはあまり興味がなく、実際にはstub ...が必要ですが、モックフレームワークを使用してスタブを作成できます。スタブの代わりにモックすることで脆弱なテストを作成するという罠。しかし、それを正しく行うのは難しいバランスです。
私見、あなたの同僚が意味することは、あなたが 実装ではなく、インターフェースへのプログラム であるべきだということです。自分のクラスをモックする頻度が高すぎる場合は、アーキテクチャを設計するときに以前の原則に違反したことを示しています。
モッククラス(モックインターフェイスとは対照的)は、モックがまだバックグラウンドに実際のクラスを持ち、継承されており、実際の実装がテスト中に実行されるである可能性があるため、不適切です。
インターフェイスをモック(またはスタブなど)する場合、実際にモックしたいコードが実行されるリスクはありません。
また、クラスをモックすることで、すべてをモックする可能性があるvirtualにする必要があります。これは非常に煩わしく、悪いクラスの設計につながる可能性があります。
クラスを分離したい場合、クラスはお互いを知らないはずです。これが、クラスの1つをモック(またはスタブなど)することが理にかなっている理由です。とにかくインターフェースに対して実装することをお勧めしますが、これは他の人たちによって十分に言及されています。
一般的に、インターフェースをモックしたいと思うでしょう。
通常のクラスをモックすることは可能ですが、それはクラスの設計に影響を与えてテストしにくい傾向があります。 アクセシビリティ、メソッドが仮想かどうかなどの懸念はすべて、真のOOの懸念ではなく、クラスをモックする機能によって決定されます。
TypeMock Isolatorと呼ばれる偽のライブラリが1つあり、これらの制限(ケーキを食べる、ケーキを食べる)を回避できますが、かなり高価です。テストしやすいように設計する方が良い。
私はモックとスタブについて Martin Fowlerによって定義されています について話しているのですが、それもあなたの同僚の意図したことだと思います。
テストの過剰仕様につながる可能性があるため、モッキングは不適切です。可能であればスタブを使用し、モックを避けます。
(上記の記事からの)モックとスタブの違いは次のとおりです。
次に、このようにスタブで状態検証を使用できます。
class OrderStateTester... public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); MailServiceStub mailer = new MailServiceStub(); order.setMailer(mailer); order.fill(warehouse); assertEquals(1, mailer.numberSent()); }
もちろん、これは非常に単純なテストです-メッセージが送信されたことだけです。私たちはそれが適切な人に、または適切なコンテンツで送信されたことをテストしていませんが、ポイントを説明するために行います。
モックを使用すると、このテストはかなり異なります。
class OrderInteractionTester... public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); Mock warehouse = mock(Warehouse.class); Mock mailer = mock(MailService.class); order.setMailer((MailService) mailer.proxy()); mailer.expects(once()).method("send"); warehouse.expects(once()).method("hasInventory") .withAnyArguments() .will(returnValue(false)); order.fill((Warehouse) warehouse.proxy()); } }
スタブで状態検証を使用するには、検証を支援するために、>スタブで追加のメソッドをいくつか作成する必要があります。その結果、スタブはMailServiceを実装しますが、追加のテストメソッドを追加します。
フレームワークをあざけることはできるだけ避けてください。同時に、できるだけテスト用にモック/偽のオブジェクトを使用することをお勧めします。ここでの秘訣は、実際のオブジェクトと一緒に組み込みの偽のオブジェクトを作成する必要があることです。私はそれについて書いたブログ投稿でより詳細に説明しています: http://www.yegor256.com/2014/09/23/built-in-fake-objects.html
テストを開発ライフサイクルの早い段階で記述できるように、クラスをモックするのは理にかなっています。
具体的な実装が利用可能になっても、モッククラスを使い続ける傾向があります。また、システムの一部が構築されていないプロジェクトの早い段階で必要なモッククラス(およびスタブ)に対して開発する傾向もあります。
システムの一部が構築されたら、それに対してテストし、それに対して(回帰のために)テストを続ける必要があります。この場合、モックから始めるのは良いですが、実装を優先してできるだけ早く破棄する必要があります。実装ではなくモックの動作に対してさまざまなチームが開発を続けているため(使用可能になった後)、プロジェクトが苦労するのを見てきました。
モックに対してテストすることで、モックがシステムの特性であると想定します。多くの場合、これには、モックされたコンポーネントが何をするかを推測することが含まれます。モックしているシステムの仕様がある場合は、推測する必要はありませんが、多くの場合、「現況」のシステムは、構築中に発見された実際的な考慮事項のために元の仕様と一致しません。アジャイル開発プロジェクトは、これが常に起こると想定しています。
次に、モックで動作するコードを開発します。モックが実際のビルド済みシステムの動作を真に表していないことが判明した場合(例:モックには見られないレイテンシの問題、モックには見られないリソースと効率の問題、同時実行の問題、パフォーマンスの問題など)維持しなければならない価値のない模擬テストがたくさんあります。
開発の開始時にはモックを使用することは価値があると思いますが、これらのモックはプロジェクトのカバレッジに貢献すべきではありません。モックが削除され、適切な統合テストが作成されてモックが置き換えられない場合は、モックがシミュレートしなかった(または実際のシステムに対して誤ってシミュレートした)さまざまな条件に対してシステムがテストされないのが最適です。
したがって、問題はモックを使用するかどうかです。それらをいつ使用し、いつ削除するかが問題になります。
実践に関するほとんどの質問と同様に、答えは「依存する」です。
モックの使いすぎは、実際には何もテストしないテストにつながる可能性があります。また、特定の実装に緊密にバインドされた、テスト中のコードの仮想的な再実装であるテストにつながる可能性もあります。
一方、モックとスタブを慎重に使用すると、単体テストが適切に分離され、1つと1つだけをテストすることができます。これは良いことです。
それは節度についてのすべてです。