14

TDD の初心者として、コレクションを扱う単体テストの作成に苦労しています。たとえば、現時点では、次のメソッドを本質的にテストするためのいくつかのテスト シナリオを考え出そうとしています。

int Find(List<T> list, Predicate<T> predicate);

listメソッドがpredicate に一致するリスト内の最初の項目のインデックスを返す場所predicate。これまでのところ、私が思いついた唯一のテストケースは、

  • アイテムが含まれていない場合list- 返品-1
  • list一致する項目が 1 つ含まれている場合predicate- を返します0
  • list一致しない項目が 1 つ含まれている場合predicate- 戻る-1
  • list両方が一致する 2 つの項目が含まれている場合predicate- 戻る0
  • 2 つの項目が含まれている場合list、最初の項目が一致するpredicate- 戻る0
  • 等...

ただし、ご覧のとおり、これらのテスト ケースは多数あり、実際に必要な実際の動作を十分にテストしていません。私の中の数学者は、誘導によるある種の TDD を実行したいと考えています。

  • アイテムが含まれていない場合list- 返品-1
  • listN 個の項目が含まれている場合、最初の項目を呼び出してから、残りの N-1 個のpredicate項目を再帰的に呼び出しますFind

ただし、これにより不要な再帰が発生します。上記のメソッドの TDD でどのような種類のテスト ケースを作成する必要がありますか?


余談ですが、私が実際にテストしようとしているメソッドはFind、単に特定のコレクションと述語 (個別にテスト ケースを作成できる) のためのものです。Find確かに、上記のテストケースのいずれかを記述する必要がなくなり、代わりに、メソッドが他の実装 (例: FindIndex) を正しい引数で呼び出すことをテストするだけの方法があるはずです。

いずれにせよ、この場合は必要ないことが判明したとしても、ユニットテスト(またはそれに似た別の方法)の方法を知りたいことに注意してください。Find

4

5 に答える 5

9

find() が機能している場合、述語に一致する最初の要素のインデックスを返す必要がありますよね?

したがって、空のリストの場合、一致しない要素の場合、および一致する要素の場合のテストが必要になります。私はそれで十分だと思います。TDDing find() の過程で、特別な最初の要素が通過するケースを書くかもしれませんが、これは簡単に偽造できます。私はおそらく次のように書くでしょう:

emptyListReturnsMinusOne()
singlePassingElementReturnsZero()
noPassingElementsReturnsMinusOne()
PassingElementMidlistReturnsItsIndex()

そして、そのシーケンスが私の正しい実装を促進することを期待してください。

于 2012-07-05T14:05:46.313 に答える
2

恐怖が退屈に変わったらテストをやめる - ケント・ベック

この場合、テストに合格する確率はいくらか

  • 「リストに述語に一致する 2 つの項目が含まれている場合 - 0 を返す」

次のテストは失敗しますか?

  • 「リストに 5 つの項目が含まれ、その両方が述語に一致する場合 - 0 を返す」

複数の要素に対して動作が機能しないのではないかと心配しているため、前者を書きます。ただし、2 が機能すると、5 に対して別のものを作成するのは退屈です (製品コードにハードコードされた 2 の仮定がない限り.. リファクタリングする必要があります。そうでない場合でも、既存のテストを次のように変更します。 2 の代わりに 5 を使用して、一般的なケースで機能するようにします)。

したがって、大幅に異なるものに対するテストを作成します。この場合、(ゼロ、1、多数) 要素と (含む/含まない) オペランドのリスト

于 2012-07-06T05:53:03.273 に答える
0

余談ですが、私は Rhino モックの経験はありませんが、FakeItEasy(?) に似たものが必要だと思います。

var finder = A.Fake<IMyFindInterface>();

// ... insert code to call IMyFindInterface.Find(whatever) here

A.CallTo(() => finder.find(A<List>.That.Matches(
                  x => x.someProperty == someValue))).MustHaveHappened();

インターフェイスの背後に Find() の実装を配置し、そのインターフェイスを使用するメソッドに偽物を渡すことで、メソッドが特定のパラメーターで呼び出されていることを確認できます。( MustHaveHappended()は、予期された呼び出しが完了していない場合、テストを失敗させます)。

IMyFindInterfaceの実際の実装は、既に信頼している実装に呼び出しを渡すだけであることがわかっているため、これは、テストしているコードが Find 実装を正しい方法で呼び出すことを確認するのに十分なテストです。

この同じ手順は、コード (テストしているユニット) が、そのコンポーネント自体を抽象化することによって、既に信頼しているコンポーネントを正しい方法で呼び出すようにしたい場合にいつでも使用できます。

于 2012-07-06T06:13:40.300 に答える
0

リストを変更しないで、述語を変更してください。

メソッドがどのように呼び出されるかを考えてください。誰かがFindメソッドを呼び出すとき、彼らはすでにリストを持っており、述語を考える必要があります。の動作を示す良い例を考えてみましょうFind:

例:すべてのテストケースに 同じリスト3, 4を使用すると、理解しやすくなります。

  1. 述語< 5は両方の数値に一致します (戻り値1)
  2. 述語== 3一致3( を返す0)
  3. 述語== 0はどれにも一致しません ( を返します-1)

動作を指定するために必要なのはこれだけです。リストではなく述語を変更することで、メソッドの使用方法の良い例が得られますFind。0 個、1 個、または 2 個の要素を持つリストは、メソッドの動作を実際に変更したりFind、メソッドの使用方法を変更したりすることはありません。テストケースで DRY に従ってください。コードが正しいことを証明しない動作を指定することに集中してください。そうしないと、テストの作成にすべての時間を費やすことになります。

于 2012-07-05T14:14:00.273 に答える
0

メソッドの要件に基づいて、Findテストするものは次のとおりです。

  1. listis null- スローArgumentNullExceptionまたはリターン-1
  2. listアイテムを含まない - 返品-1
  3. predicateis null- スローArgumentNullExceptionまたはリターン-1
  4. listpredicate- の戻り値と一致しない項目が 1 つ含まれています-1
  5. listpredicate- の戻り値に一致する 1 つの項目を含む0
  6. list複数のアイテムが含まれていますが、一致するアイテムはありませんpredicate- 戻り値-1
  7. listに一致する複数のアイテムが含まれていますpredicate- 最初に一致したインデックスを返します

基本的に、最初に最終ケース (null 引数、空のリスト) をテストします。その後、1項目テスト。最後に、複数のアイテムの一致と不一致をテストします。

引数については、好みに応じて、null例外をスローするか、または返すことができます。-1

于 2012-07-05T14:08:29.610 に答える