2013-04-25 104 views
5

我正在寻找以下单元测试用例的解决方案/模式。单元测试模式

案例:

让我们假设我们有三类,A,B,C每一个方法。 A的方法调用B的方法调用C的方法。所以,A-> B-> C。每种方法都有一个输入(方法A的输入A,输入B,输入C)。将得到的调用方法A的输出将是一个树结构,例如:

根(从方法A创建) - 节点B(从方式B创建) - 节点C1 - 节点C2 (都是从方法C创建的)

对于我来说,单元测试是关于测试单独输入方法的输出。所以,我们会为上面的每个方法编写单元测试。因为这些测试是独立编写的,所以在为方法A编写单元测试时,我们会模拟方法B,并且在编写方法B的单元测试用例时模拟方法C.

到目前为止,一切都很好,我们可以编写对每种方法的输出的期望,以确保树结构得到尊重。

问题:

现在,让我们添加另一个类称之为B法,使我们也有以下的调用链:D-> B->℃。得到的根树应该是这样的:

  • 根d
    • 节点B
      • 节点C1
      • 节点C2

在发展过程中,有人意识到要求方法A被误解了,树结果应该是这个样子的:

    • 节点B
      • 节点C

令人高兴的是,开发者将改变方法C,以便输出只返回一个节点而不是两个。他会改变单元测试,以反映这些变化。 然而,方法D的要求不应该改变,并且该方法的输出应该仍然具有节点C1和节点C2。

问题:

你怎么会写单元测试,以使第二开发人员已经惊动突破,他将相继推出了针对方法d的变化?我宁愿避免在这里看来最适合的集成测试。

谢谢。

回答

0

方法如果你想坚持独立的单元测试(这是我对大多数做我的中心物体,因为我发现几乎不可能用适当的深度写出适量的综合测试,并保持它们的快速),一个有趣的方法是Joe Rainsberger的Contract and Collaboration Tests。他提出了这样的观点:除非您有相应的合同测试,确保该依赖关系的具体实现确实在现实世界中表现出这种行为,否则您不应该嘲笑依赖性以在测试中表现某种方式。

在您的示例中,说“方法C现在只返回一个节点而不是两个”意思是更改C的合同及其合同测试。这就引入了模拟C与新契约不同步的可能性,因此你应该在你的测试中查找C被嘲笑,并在需要时调整模拟。

此后现有的测试的仔细检查会继而导致你意识到,你真的不希望改变方法C合同,而是引入新的方法C2适合A.

的需求

Rainsberger描述了他使用的过程,以确保他在测试here中获得了相互作用。您还可以在this video中观看技术。

1

理想的单元测试应该贯穿一路下跌到C类一单元测试并不一定意味着的一种方法一类。它只是意味着你可以运行它而不需要像库或数据库等其他依赖项。然后你需要一个集成测试。

你的单元测试应该一直贯穿到C,并且当A的需求发生变化时,单元测试将会中断,而D的开发人员将知道某些错误。

+0

单位是指“一个逻辑单位”。在这种情况下,您在D或A上测试的行为部分由C定义,因此请将其包含在您的单元中。 – dascandy 2013-04-25 10:51:33

2

您应该混合使用“pure¹”单元测试,不纯的单元测试,集成(外部资源击中)测试和完整堆栈测试(UI下拉)。 “纯”单元测试不是测试的结束。

所以,你最终会与一些后续的测试类型的每一个功能,您开发:

  • 一个单元测试 - 创下孤立 - 如果下级复杂或撞击外部资源。
  • 单元测试(但有时也称为集成或组件测试) - 在外部资源(例如数据库)之前将A降至(近似)级别 - 如果较低级别较为复杂,但经过测试或较低级别并不复杂。
  • 集成测试 - 命中A和数据库。
  • 完整的堆栈测试 - 点击恰好在某个时候调用A的UI - 我的目标是在这个级别(如Cukes之类)或下面的级别至少有几个。

只要你有一些更高级别的测试,他们应该失败,即使测试命中隔离没有。

¹只是用短语“纯单元测试”有些人有它的头上有定义单元测试是在隔离

+0

“纯”是什么意思?只有一堂课? – 2013-04-25 11:07:14

+0

是的。它不是我所定义的“单元测试”,但有些人是这样做的,我给它命名。 – 2013-04-25 12:42:38