2009-08-31 8 views
0

まず、TDDのかなり新しい開業医であるにもかかわらず、私はそのメリットをかなり売却していると述べます。私はモックを使うことを検討するのに十分なほどの進歩を遂げたように感じ、OOPでモックがどこに合っているかを理解するために真のレンガの壁に当たった。もう1つの疑問の質問

私は見つけられたほど多くの関連する記事/記事を読んできましたが(FowlerMiller)、模擬方法や時期についてはまだ完全にはっきりしていません。

具体例を挙げておきます。私のアプリケーションには、特定のユースケースに大まかにマップされたサービスレイヤークラス(アプリケーションレイヤーと呼ばれるものもあります)があります。これらのクラスは、パーシスタンス層、ドメイン層、さらには他のサービスクラスと連携することができます。私は良い小さなDIの男の子してきた、彼らはこのようになりますテストの目的など

サンプルサービスクラスのために下塗りしできるように適切に私の依存関係を因数分解している:

public class AddDocumentEventService : IAddDocumentEventService 
{ 
    public IDocumentDao DocumentDao 
    { 
     get { return _documentDao; } 
     set { _documentDao = value; } 
    } 
    public IPatientSnapshotService PatientSnapshotService 
    { 
     get { return _patientSnapshotService; } 
     set { _patientSnapshotService = value; } 
    } 

    public TransactionResponse AddEvent(EventSection eventSection) 
    { 
     TransactionResponse response = new TransactionResponse(); 
     response.Successful = false; 

     if (eventSection.IsValid(response.ValidationErrors)) 
     { 

      DocumentDao.SaveNewEvent(eventSection, docDataID); 

      int patientAccountId = DocumentDao.GetPatientAccountIdForDocument(docDataID); 
      int patientSnapshotId =PatientSnapshotService.SaveEventSnapshot(patientAccountId, eventSection.EventId); 

      if (patientSnapshotId == 0) 
      { 
       throw new Exception("Unable to save Patient Snapshot!"); 
      } 

      response.Successful = true; 
     } 
     return response; 
    } 

}

私はNMockを使って、このメソッドを依存関係(DocumentDao、PatientSnapshotService)とは別にテストしています。次のようにここでテストをからかっに

[Test] 
public void AddEvent() 
    { 
     Mockery mocks = new Mockery(); 
     IAddDocumentEventService service = new AddDocumentEventService(); 
     IDocumentDao mockDocumentDao = mocks.NewMock<IDocumentDao>(); 
     IPatientSnapshotService mockPatientSnapshot = mocks.NewMock<IPatientSnapshotService>(); 

     EventSection eventSection = new EventSection(); 

     //set up our mock expectations 
     Expect.Once.On(mockDocumentDao).Method("GetPatientAccountIdForDocument").WithAnyArguments(); 
     Expect.Once.On(mockPatientSnapshot).Method("SaveEventSnapshot").WithAnyArguments(); 
     Expect.Once.On(mockDocumentDao).Method("SaveNewEvent").WithAnyArguments(); 

     //pass in our mocks as dependencies to the class under test 
     ((AddDocumentEventService)service).DocumentDao = mockDocumentDao; 
     ((AddDocumentEventService)service).PatientSnapshotService = mockPatientSnapshot; 

     //call the method under test 
     service.AddEvent(eventSection); 

     //verify that all expectations have been met 
     mocks.VerifyAllExpectationsHaveBeenMet(); 
    } 

この小さな進出に私の考えどのように見えるのは、次のとおりです。

  1. このテストでは、カプセル化されていない以上、そのうちの多くの基本的なオブジェクト指向の戒律を破るために表示されます。私のテストテスト対象のクラス(つまりメソッド呼び出し)の具体的な実装の詳細を完全に認識しています。クラス内部が変わるたびにテストを更新するのに費やされる時間は、非生産的です。
  2. 私のサービスクラスは現時点ではかなり単純化されているかもしれませんが、これらのテストでどのような価値が追加されたのかは分かりません。特定のユースケースが指示するように共同オブジェクトが呼び出されていることを保証していますか?このような小さな利益のために、コードの重複は不当に高いようです。

私は何が欠けていますか?

+0

「何が欠けていますか?」 - 冗長コードを除去するためにリファクタリングをテストしますか?つまり、一般的な手順をテスト設定に移動します。 – tvanfosson

答えて

2

あなたはmartin fowlerの非常に良い投稿を件名に記載しました。彼が言及している一つの点は、モックが行動をテストして物を分離することを好む人であるということです。

"古典的なTDDスタイルは、可能であれば実際のオブジェクトを使用し、実際のものを使用するのが面倒であれば倍増するため、古典的なTDDerはメールサービスに実際の倉庫とダブルを使用します。ない本当に重要というくらい。

mockist TDD実践者、しかし、常に興味深い振る舞いを持つ任意のオブジェクトのモックを使用します。この場合、倉庫やメールサービスの両方のために。

この種のものが気に入らなければ、おそらく古典的なTDDerであり、 (メールサービスやクレジットカードのチャージのように)厄介です。 それ以外の場合は、(メモリ内データベースの作成など)を作成します。

特に私はモチストですが、(値を返さない限り)特定のメソッドが呼び出されているかどうかはほとんど検証しません。いずれにしても、私はインターフェイスをテストしています。関数が何かを返すとき、私はmockingフレームワークを使ってスタブを作成します。

最後に、すべてが何であり、どのようにテストしたいのですか。それらのメソッドが実際に呼び出されたかどうかをチェックすることが重要であると思いますか?通話の前後の状態を確認したいですか(偽物を使用しますか?) それが働いていると考えるには十分なものを選び、それを正確にチェックするためにテストを構築してください!あなたが通常よりよい設計を得るTDDときに時間がかかるかもしれませんが、

    短期間で
  • :テストの値について

    は、私はいくつかの意見を持っています。長期的には

  • あなたは(あなたが本当によく詳細は覚えていないだろうと)後でこのコードを変更し、維持するには余りにも恐れないだろう、とあなたは、すぐにほとんど即座にフィードバックを赤を取得します

ちなみに、テストコードサイズはプロダクションコードサイズほど大きいのが普通です。

+0

"[snip](値を返さない限り)特定のメソッドが呼び出されているかどうかはほとんど検証されませんが、いずれにせよ、私はインターフェイスをテストしています[0123]。 私はdidnテスト中のメソッドによって呼び出されたオブジェクトのスタブ/モックはオプションであるとは思わない。例えば、私が上で提供したサンプルシナリオで、共用オブジェクトのスタブまたは実際の実装を提供しないと、テストが悲惨に失敗しないでしょうか? –

0

これらのタイプのテストでは、偽の(永続的な)パーシスタンス層を持つことが有用であることがわかりました。次に、特定の呼び出しが行われたことを確認する代わりに、最終結果(リポジトリにアイテムが存在すること)を確認できます。あなたがモックを使って作業しているのは分かっていますが、私はこれが最良の場所だとは思わないと言います。これは、実装の詳細を知らないあなたのテストを続けて

Instantiate the fake repositories. 
Run your test method. 
Check the fake repository to see if the new elements exist in it. 

:ような例として

は、このテストのための擬似コードは、私が見るであろう。しかし、それは偽のパーシスタンス層を維持することを意味するので、考慮する必要があるトレードオフだと思います。

1

カプセル化を破ってテストをコードに強く結びつけることは、間違いなくモックを使用することの欠点となります。テストがリファクタリングに対して脆弱であることは望ましくありません。これはあなたが歩かなければならない細い線です。私は個人的にはモックの使用を避けます。 コードを見ると、まずBDDスタイルを使用します。テストメソッドはメソッドの特定の動作をテストする必要があります。そのような名前を付ける必要があります(たとえば、AddEventShouldSaveASnapshotなど)。第二に、経験則は、起こったはずのすべてのメソッド呼び出しをカタログ化するのではなく、予想される動作が起こったかどうかを確認することです。

+0

興味をそそられる音。どのように私はオブジェクトのcolloboratersのためのモックを提供せずに含まれているサンプルの期待された動作を確認するだろうか?同じように、クリスは以下の答えで説明していますか? –

0

コード内のステークホルダーを分離することは時々価値があります。

カプセル化は、潜在的な依存性の数を、変化の伝搬の可能性が最も高い状態で最小限に抑えることです。これは静的ソースコードに基づいています。

ユニットテストは、実行時の動作が意図せず変更されないようにすることです。スタティックソースコードに基づいていません。

ユニットテスターが元のカプセル化されたソースコードではなく、すべてのプライベートアクセサが自動的にパブリックアクセサリーに変更されたソースコードのコピーで作業する場合に便利なことがあります(これはちょうど4行のシェルですスクリプト)。

これは、カプセル化を単体テストと明確に区​​別します。

単体テストのテストの方法はいくつありますか?テストするメソッドの数はいくつかあります。これは味の問題です。カプセル化(ただし、ユニットテストで何も)の詳細について

、以下を参照してください http://www.edmundkirwan.com/encap/overview/paper7.html

1

をテストモックとあなたが協力し、オブジェクト間の関係を理解するのに役立つはずですが—、それらが互いに通信する方法を説明したプロトコル。この場合、イベントが到着したときにいくつかのことが持続されることを知りたいとします。オブジェクトの外部関係を記述している場合、カプセル化を破ることはありません。 DAOとサービスタイプは外部サービスを記述しますが、実装方法を定義しません。

これはほんのわずかな例ですが、コードはオブジェクト指向ではなく手続き的であると感じています。 1つのオブジェクトから抽出され、別のオブジェクトに渡される単純な値のいくつかのケースがあります。おそらく、何らかの種類のPatientオブジェクトがイベントを直接処理する必要があります。難しいと言えますが、おそらくテストではこれらの設計上の問題が示唆されていますか?一方

、あなたが自己宣伝を気にしないと、別の月を待つことができれば、 http://www.growing-object-oriented-software.com/

1

私はこのようなテストを書くとき、私は同じ違和感を持っています。関数本体に期待をコピーしてcopy'nを使って関数を実装すると、特に私は驚いています(これは、モック用にLeMockを使用するときに機能します)。

でもOKです。それは起こる。このテストでは、テスト中のシステムがどのように依存関係と相互作用しているかを文書化し、検証します。これは良いことです。しかし、このテストには他にも問題があります。

  1. 一度に多くのテストをしています。このテストでは、3つの依存関係が正しく呼び出されていることを確認します。これらの依存関係のいずれかが変更された場合は、このテストを変更する必要があります。それぞれの依存関係が適切に処理されていることを確認し、3つの別々のテストを行う方が良いでしょう。テストしていない依存関係のスタブオブジェクトを渡します(失敗するとモックではなく)。

  2. これらのテストは不完全であるため、依存関係に渡されるパラメータの検証は行われません。

このサンプルでは、​​mockingライブラリとしてMoqを使用します。このテストはすべての依存関係の動作を指定するものではなく、1つの呼び出しのみをテストします。また、渡されたパラメータが入力時に期待されるものであることを確認します。入力のバリエーションは、別々のテストを正当化します。

渡される未使用の依存関係は、AddEvent()がそれらを使用する場合にのみ必要であることに注意してください。非常に合理的に、このクラスにはこのテストに関係しない依存関係が表示されているかもしれません。

+0

フィードバックいただきありがとうございます。あなたが上げる2つの大きなポイントについて詳しく説明できますか?たぶんあなたが時間があれば少しの擬似コードで。 –