TDD - 为迭代/使用集合的方法编写测试

时间:2012-07-05 13:41:41

标签: c# collections tdd

作为TDD的新手,我正在处理处理收藏的写作单元测试。例如,目前我正在尝试提出一些测试场景来基本上测试以下方法

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

该方法应返回列表list中与谓词predicate匹配的第一项的索引。到目前为止,我能够提出的唯一测试案例就是

  • list不包含任何内容时 - 返回-1
  • list包含与predicate匹配的1项时 - 返回0
  • list包含1个与predicate不匹配的项目时 - 返回-1
  • list包含两件与predicate匹配的商品时 - 返回0
  • list包含2个项目时,其中第一项与predicate匹配 - 返回0
  • 等...

然而,正如您所看到的,这些测试用例数量众多且无法令人满意地测试我实际需要的实际行为。我的数学家想要做一些TDD-by-induction

  • list不包含任何内容时 - 返回-1
  • list包含N个项目时,在第一个项目上调用predicate,然后在其余的N-1个项目上递归调用Find

然而,这会引入不必要的递归。对于上述方法,我应该在TDD中编写哪种测试用例?


另外,我试图测试的方法实际上只是Find,只是针对特定的集合和谓词(我可以独立编写测试用例)。当然,我应该有办法避免编写任何上述测试用例,而只是测试该方法是否使用正确的参数调用其他Find实现(例如FindIndex)?

请注意,在任何情况下,我仍然想知道如何我可以单元测试Find(或类似的其他方法),即使事实证明在这种情况下我不喜欢不需要。

5 个答案:

答案 0 :(得分:9)

如果find()工作,那么它应该返回与谓词匹配的第一个元素的索引,对吗?

因此,您需要对空列表案例进行测试,对于无匹配元素案例需要测试,对于匹配元素案例需要测试。我会发现足够了。在TDDing find()的过程中,我可能会写一个特殊的first-element-pass案例,我可以很容易地伪造。我可能会写:

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

并期望该序列将推动我的正确实施。

答案 1 :(得分:2)

  

当无聊取代恐惧时停止测试 - 肯特贝克

在这种情况下,给出

的通过测试的概率是多少
  • “当列表包含两个匹配谓词的项目时 - 返回0”

以下测试会失败吗?

  • “当列表包含5个匹配谓词的项目时 - 返回0”

我会写前者,因为我担心这种行为对多个元素不起作用。然而,一旦2个工作,为5编写另一个工作只是乏味(​​除非在生产代码中存在2个硬编码假设...应该已经重构了。即使不是,我只是将现有测试修改为有5而不是2,并使其适用于一般情况。)

因此,针对显着不同的事情编写测试。在这种情况下,列出(零,一,多)元素和(包含/不包含)操作数

答案 2 :(得分:0)

根据您对Find方法的要求,这是我要测试的内容:

  1. listnull - 抛出ArgumentNullException或返回-1
  2. list不包含任何内容 - 返回-1
  3. predicatenull - 抛出ArgumentNullException或返回-1
  4. list包含一项与predicate不匹配的项目 - 返回-1
  5. list包含一个与predicate匹配的项目 - 返回0
  6. list包含多个项目,但没有项目与predicate匹配 - 返回-1
  7. list包含多个与predicate匹配的项目 - 返回首次匹配的索引
  8. 基本上,您首先要测试最终用例 - 空参数,空列表。之后,一个项目测试。最后,测试匹配和不匹配多个项目。

    对于null个参数,您可以抛出异常,也可以返回-1,具体取决于您的偏好。

答案 3 :(得分:0)

请勿更改列表,更改谓词

考虑如何调用该方法。当有人调用Find方法时,他们已经有了一个列表,需要考虑谓词。因此,请考虑一些证明Find

行为的好例子

示例: 对所有测试用例使用相同的列表3, 4使其易于理解:

  1. 谓词< 5匹配两个数字(返回1
  2. 谓词== 3匹配3(返回0
  3. 谓词== 0匹配无(返回-1
  4. 这就是指定行为所需的全部内容,通过更改谓词而不是列表,您可以提供有关如何使用Find方法的良好示例。包含零个,一个或两个元素的列表并没有真正改变Find的行为,也没有真正改变该方法的使用方式。跟随你的测试用户使用DRY,专注于指定不证明代码正确的行为,否则你最终会花费所有时间来编写测试。

答案 4 :(得分:0)

试着回答你的问题:我对犀牛嘲笑没有任何经验,但我相信它应该有类似于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-以正确的方式实施。

只要您想要确保您的代码(您正在测试的单元)通过抽象掉该组件本身来正确地调用您已经信任的某个组件,就可以使用相同的过程 - 正是我们在单元测试时所需要的。