我一直在听说测试驱动开发(TDD)的伦敦风格与芝加哥风格(有时称为底特律风格)。

犹他州极限编程用户小组的工作坊:


交互风格的TDD也被称为嘲笑风格,或在伦敦的Extreme Extreme Tuesday俱乐部流行后的伦敦风格。它通常与底特律风格或经典的TDD形成对比,后者更基于状态。


Jason Gorman的工作室:


该讲习班涵盖了芝加哥的TDD学校(基于状态的行为测试和三角测量)和伦敦的学校,而伦敦学校则更侧重于交互测试,模拟和端到端TDD,其中
特别强调了责任驱动的设计和Tell,Do n't Ask的OO方法,最近由Steve Freeman的和Nat Pryce出色的由对象指导的日益增长的面向对象软件重新流行。 >测试书。


发布经典TDD还是“伦敦学校”?杰森·戈尔曼(Jason Gorman)的著作很有帮助,但是他的例子使我感到困惑,因为他使用两个不同的例子,而不是同时使用两种方法的一个例子。有什么区别?您何时使用每种样式?

#1 楼

假设您有一个称为“分类帐”的类,一个名为“计算”的方法,该方法使用“计算器”根据传递给“计算”的参数进行不同类型的计算,例如“ multiply(x,y)”或“ subtract( x,y)“。

现在,假设您要测试调用ledger.calculate(” 5 * 7“)时发生的情况。

伦敦/互动学校会让您断言是否调用了Calculator.multiply(5,7)。各种模拟框架对此很有用,例如,如果您没有“ Calculator”对象的所有权(假设它是您无法直接测试的外部组件或服务,但您可以这样做,那么它就非常有用)。知道您必须以特定的方式调用。)

芝加哥/州立学校会要求您确定结果是否为35。jUnit / nUnit框架通常适合于这样做。

都是有效且重要的测试。

评论


很好的例子。

–sevenseacat
2011年12月7日下午4:36

我将添加使用它们的更多原因:如果重要的事情是根据所采取的措施来确定某事物已更改或未更改(例如,在ledger.calculate(“ 5 * 7“)被调用),您想使用状态声明(芝加哥学校)。当您在调用该方法之前完全控制系统状态以及实际控制该方法执行操作时,这将非常有用。

–马修·弗林(Matthew Flynn)
2011年12月7日在20:05

如果重要的是知道第二个方法被调用(例如Calculator.multiply(5,7)),则您想使用活动断言,就像通过模拟对象一样。如果所调用的方法具有所需的副作用(例如,保存数据,增加计数器,发送消息等),如果您没有真正控制该方法的作用,那么此方法将非常有帮助,因此返回值可能不一致。另外,如果您不能轻松控制系统状态,则最好的办法就是确定发生了什么活动。

–马修·弗林(Matthew Flynn)
2011年12月7日在20:05

如果您想断言分类帐是否正确地使用了特定的接口,不管它是在运行时由计算器,RemoteCalculator还是ThreadsafeCalculator等实现的,伦敦学校也很有用。如果进行了无效的方法调用,则可以初始化Mock来引发到界面。系统测试应断言包含实例化分类账->计算器对的系统会产生正确的结果,但分类账的单元测试不应使用依赖于计算器的创建或行为(及其所有依赖项)。

–乔纳森·哈特利
2015年3月18日在9:24



伦敦方法还主张提供更清晰的反馈信号,因为如果计算器乘以倍数进行回归,您将看到两个测试失败:分类帐测试和计算器测试,但是如果您模拟出计算器,则只有一个测试失败。这样可能更容易查明错误的来源,尤其是在系统复杂的情况下。

–马特西亚
16年1月13日在10:33

#2 楼

Martin Fowler的文章Mocks Are n't Stubs是对该主题的很好的介绍。

根据您选择的设计样式(以及构建程序所依据的设计原理),至少有两种查看对象的方式:


作为基于输入执行计算的单元。作为这种计算的结果,对象可以返回一个值或更改其状态。
作为一个活动元素,它通过消息传递与系统中的其他元素进行通信。

在第一种情况下,您对处理产生的结果或处理后对象处于哪种状态感兴趣。这是assertEquals()之类的方法输入图像的地方。在这种情况下,处理中还涉及其他什么对象,调用了哪些方法等等都没关系。这种验证称为基于状态的验证,是“经典”样式。

在第二种情况下,由于大多数对象甚至都没有返回任何结果(例如Java中的void方法),因此您对对象之间如何通信以及在测试所施加的环境下它们是否传递正确的消息更加感兴趣。这些交互通常在模拟框架的帮助下进行验证。这种验证称为基于行为的验证或基于交互的验证。它的含义之一是称为行为驱动开发的技术,您可以通过该类在假设其协作者已经存在的情况下开发一个类(即使它们可能不存在),因此可以针对其接口进行编码。

请注意,这不是一个选择。您可以拥有将两种方法结合在一起的设计风格,以充分利用每种方法的优点。