7

JUnitとTDDについてもっと知りたいのですが、テストケース間の結合に関していくつかの問題が発生しています。

特定のデータ型のAPIのテストケースを作成している場合、たとえばDeque<T>、テストケース間の結合を制限するにはどうすればよいですか?たとえば、メソッドのテストケースを作成している場合insertFirst(T item)、適切に初期化されたオブジェクトでメソッドを呼び出した後、2つのことをアサートできるはずだと考えるのは簡単なようです。

  1. オブジェクトのサイズをDeque1つ増やす必要があります
  2. その後、対応するT removeFirst()メソッドを呼び出すと、最初の呼び出しで挿入したオブジェクトへの参照が返されます。

ただし、これにより、少なくとも2つのテストケース間に望ましくない結合が発生します。1つのテストケースの合格は、別のAPIメソッドの正しい実装に依存します。たとえば、このテストケースに合格するには、内のアイテムの数を確認したり、アイテムDequeを削除したりするための正しい実装が必要になります。これらの方法のいずれかのテストが何らかの理由で正しくないか不完全である場合、そのinsertFirst方法のテストは自動的に疑わしいものになります。

このシナリオを回避するためのベストプラクティスは何ですか?テストケースを書くための私のアプローチは、何らかの形で間違っていますか?

4

2 に答える 2

13

1つのメソッドのテストを作成するときは、クラスの残りの部分が正しく機能していると想定する必要があります。この仮定を行わない場合、唯一の結論は、クラスごとに1つの大規模なテストになります。そして、それは私たちがしていることではありません。

クラスの他の部分も正しく機能していると想定できます。これらの他の部分についてもテストが行​​われ、それらの正確性が確認されるためです。
一部が正しく機能していない場合、テストは失敗し、何かが正しくないことが示されます。
テストスイートのテストが失敗するとすぐに、修正する必要のあるエラーが発生します。あなたはもはやいかなる仮定もすることができません。

例:

次の3つのメソッドのみを使用した単純なリスト実装があります。

  1. 入れる
  2. 削除する
  3. カウント

3つのテストがあります。

  1. のテストinsert
    • リストのインスタンスを作成する(Arrange
    • insertアイテム(行為
    • count1に等しいことを確認します(アサート
  2. のテストremove
    • リストとinsertアイテムのインスタンスを作成する(配置
    • removeアイテム(行為
    • count0に等しいことを確認します( Assert
  3. のテストcount
    • リストとinsertn個のアイテムのインスタンスを作成する(配置
    • 取得countAct
    • countnに等しいことを確認します( Assert

さて、上記のテストのいずれかが失敗した場合、クラスの1人のメンバーが正しいかどうかを確認することはできません。

  • 最初のテストが失敗すると、3番目のテストも失敗します。remove2つ目は合格しますが、削除するものがなかったため、実際にはテストしませんでした。
  • 2番目のテストが失敗した場合でも、他の2つのテストは合格します。それでも、3つのメンバーのいずれかが正しく機能しない場合、2番目のテストは失敗するため、それが正しく機能していることを確認することはできませinsertcount
  • 3番目のテストが失敗した場合、他の2つも失敗する可能性があります。

ただし、失敗したテストは何かを教えてくれます。失敗し
たテストによっては、エラーが発生する場所を推測できることがよくあります。
例:2番目のテストのみが失敗し、1番目または3番目のテストが失敗しない場合、エラーはremoveメソッドにある可能性があります。

于 2013-02-20T12:57:10.237 に答える
4

通常、単体テストは、特定のメソッドではなく特定の機能をテストするものと考える方が生産的です。どのテストでも、いくつかのメソッドのコレクションが正しく機能して、テストの対象となる機能が実装されているかどうかがチェックされます。適切に設計された一連のテストの失敗パターンから、どのメソッドがすぐに壊れたかがわかります。

テストの優れたコレクションは、TDD の実行から自然に落ちる傾向があります。それが、このテクニックを非常に強力なものにしている理由の 1 つです。私が を書いている場合Deque、私が書くテストは次のようになる傾向があり、通常はこの順序で表示されます。

  1. empty_Deque_isEmptyisEmpty--常に返すように実装するtrue
  2. non_empty_Deque_isntEmpty--インスタンス変数を falseinsertFirstにする実装isEmpty
  3. re_emptied_Deque_isEmpty-- によって使用されるインスタンス変数を、およびisEmptyに応答する数値に変更します。insertFirstremoveFirst
  4. is_empty_Deque_size_correctsize--常に 0 を返すように実装する
  5. is_nonempty_Deque_size_correct-- トラック サイズにインスタンス変数を追加します。が必要とするのと同じことをしていることに気付きisEmptyます。リファクタリング
  6. is_re_emptied_Deque_size_correct-- 5. を実現するために私たちが行ったことにより、テストに合格しました。
  7. does_removing_from_empty_Deque_throw--他に何かをする前removeFirstに確認する必要がありますsize
  8. is_inserted_item_returned--次に、insertFirstインスタンス変数を設定しますremoveFirstT
  9. is_inserted_item_returned_from_endremoveLast--のコピーであることを追加しremoveFirstます。リファクタリング
  10. is_rear_inserted_item_returnedinsertLast--そのコピーを追加しinsertFirstます。リファクタリング
  11. are_all_inserted_items_returned-- 変更insertFirstremoveFirstて行動するSomeKindOfCollection<T>。検索の順序をチェックしないことを強調する
  12. does_removeFirst_retrieve_items_in_correct_order-- 2 つのものを挿入します。2 つ目のものが によって返されることを確認してremoveFirstください。すでに本当かもしれません。
  13. does_removeLast_retrieve_items_in_correct_order-- についても同様ですがremoveLast、まだ合格していないことがかなり確実です。

これは一連のテストですが、それらに目を通すと、パターンに気付くはずです。これらのテストはどれも、実際には「のテストcount」または「のテスト」ではありませんremoveFirst。しかし、この作業を終える頃には、クラスのインターフェース全体が実行されており、そのインターフェースに必要なすべての内部構造が開発されています。一部のテストは複数のメソッドに依存しており、そのメソッドが失敗すると、すべてが壊れます。しかし、ブレークのパターンは、バグがどこにあるかを判断するのに非常に役立ちます。

また興味深いのは、これらのテストのうち、実際にオブジェクト内にコレクションを持つことを約束する必要なく合格できるテストの数です。これは、一連のテストを、開発時に役立つ、より一般的なテスト スイートに分解できることを示唆していますPriorityQueue

于 2013-02-20T23:20:08.417 に答える