合同设计和测试驱动开发

时间:2008-12-27 02:48:07

标签: language-agnostic project-management tdd design-by-contract

我正在努力改进我们小组的开发过程,我正在考虑如何最好地实施与测试驱动开发的契约设计。看来这两种技术有很多重叠,我想知道是否有人对以下(相关)问题有所了解:

  • 除非您使用某种代码生成器根据合同生成单元测试,否则是否违反DRY原则才有TDD和DbC?否则,你必须在两个地方维持合同(测试和合同本身),或者我错过了什么?
  • TDD在多大程度上使DbC变得多余?如果我写得很好,那么它们不等同于写合同吗?如果我在运行时以及通过测试执行合同,我是否只能获得额外的好处?
  • 仅使用TDD而不是使用DbC的TDD更容易/更灵活吗?

这些问题的要点是这个更普遍的问题:如果我们已经正确地进行了TDD,如果我们也使用DbC,我们是否会获得显着的开销?

一些细节,虽然我认为这个问题主要与语言无关:

  • 我们的团队非常小,<10名程序员。
  • 我们主要使用Perl。

8 个答案:

答案 0 :(得分:35)

注意差异。

由合同驱动的

设计。合同驱动的设计

开发由测试驱动。测试驱动开发

他们的关系在于一个先于另一个。他们描述了不同抽象层次的软件。

您在实施时是否放弃了设计?您是否认为设计文件违反DRY?您是否单独维护合同和代码?

软件是合同的一种实现。测试是另一个。用户手册是第三个。操作指南是第四个。数据库备份/恢复过程是合同实施的一部分。

我无法从Design by Contract中看到任何开销

  • 如果您已经在进行设计,那么您可以将格式从太多单词更改为恰当的单词以概述合同关系。

  • 如果您没有进行设计,那么签订合同将消除问题,降低成本和复杂性。

我看不出任何灵活性的损失。

  1. 以合同开始,

  2. 然后

    一个。写测试和

    湾写代码。

  3. 了解这两个开发活动是如何交织在一起的,都来自合同。

答案 1 :(得分:26)

我认为DbC和TDD之间存在重叠,但是,我认为没有重复工作:引入DbC可能会导致测试用例减少。

让我解释一下。

在TDD中,测试不是真正的测试。它们是行为规范。但是,它们也是 设计工具:通过首先编写测试,您可以使用被测对象的外部API - 您还没有实际编写 - 与用户一样。这样,您可以以对用户有意义的方式设计API,而不是以最容易实现的方式设计API。像queue.full?而不是queue.num_entries == queue.size

第二部分不能被合同取代。

第一部分可以部分替换为合同,至少对于单元测试而言。 TDD测试用作其他开发人员(单元测试)和领域专家(验收测试)的行为规范。合同为其他开发人员,域专家以及编译器和运行时库指定行为。

但是契约具有固定的粒度:你有方法前后条件,对象不变量,模块契约等等。也许是循环变体和不变量。然而,单元测试,测试行为单位。那些可能比方法小或由多种方法组成。这不是你可以用合同做的事情。而对于“大局”,您仍然需要集成测试,功能测试和验收测试。

还有DbC没有涉及的TDD的另一个重要部分:中间D.在TDD中,测试驱动您的开发过程:除非你有,否则你永远不会写一行实现代码如果测试失败,除非测试全部通过,否则你只编写一行测试代码,只编写最少量的实现代码以使测试通过,你只需编写最少量的测试代码就可以产生失败的测试。 / p>

总之:使用测试来设计API的“流程”,“感觉”。使用合同来设计API的合同。使用测试为开发过程提供“节奏”。

这样的事情:

  1. 为功能编写验收测试
  2. 为实现该功能的一部分的单元编写单元测试
  3. 使用您在步骤2中设计的方法签名,编写方法原型
  4. 添加后置条件
  5. 添加前置条件
  6. 实施方法正文
  7. 如果验收测试通过,请转到1,否则转到2
  8. 如果你想知道契约设计的发明者Bertrand Meyer想到将TDD和DbC结合起来,他的小组就有一篇很好的论文叫做Contract-Driven Design = Test-Driven Development - Writing Test Cases。基本前提是合同提供了所有可能情况的抽象表示,而测试用例仅测试特定情况。因此,可以从合同中自动生成合适的测试工具。

答案 2 :(得分:6)

我想补充一下:

API是程序员的合同,UI定义是与客户的合同,协议是客户端 - 服务器交互的合同。首先获得这些,然后您可以利用并行开发轨道而不会迷失在杂草中。是的,定期审查以确保满足要求,但在没有合同的情况下永远不会开始新的轨道。而'合同'是一个强有力的词:一旦部署,它永远不会改变。您应该从一开始就包括版本管理和内省,合同的更改只能通过扩展集实现,版本号随之更改,然后您可以在处理混合或旧安装时执行优雅降级等操作。

我从困难的方式学到了这一课,一个大型项目徘徊在永不落地的土地上,然后在严重的枪支,公司生存,短保险时间线下以正确的方式应用它。我们定义了协议,为事务的每一侧定义并编写了一组协议仿真(基本上是固定消息生成器和接收消息检查器,一个晚上值得进行双脑编码),然后分开编写服务器和客户端该应用程序。我们重新组织了节目之夜,它刚刚起作用。要求,设计,合同,测试,代码,集成。以该顺序。重复直到烘烤。

我对TLA的设计有点怀疑。与模式一样,符合流行语的配方是一个很好的指南,但根据我的经验,没有一个通用的设计或项目管理程序。如果你正在按照书本(tm)做事情,那么,除非它是与DOD程序要求的国防部合同,否则你可能会在某个地方遇到麻烦。阅读书籍,是的,但一定要了解它们,然后再考虑你团队的人员。仅由本书强制执行的规则将不会统一强制执行 - 即使在工具强制执行时,也可能存在辍学(例如,svn注释留空或隐藏简短)。当工具链不仅强制执行它们而且使跟随比任何可能的捷径更容易时,程序往往会被遵循。相信我,当事情变得艰难时,捷径就会被发现,而你可能不知道那些在凌晨3点被使用过的东西,直到为时已晚。

答案 3 :(得分:2)

您还可以使用以合同的域语言编写的可执行验收测试。它可能不是真正的“合同”,而是单元测试和合同之间的一半。

我建议使用Ruby Cucumber http://github.com/aslakhellesoy/cucumber

但是既然你是一个Perl商店,那么也许你可以用自己的p5黄瓜小尝试。 http://github.com/kesor/p5-cucumber

答案 4 :(得分:1)

Microsoft已根据代码合同和参数化单元测试完成了自动生成单元测试的工作。例如。合同说当项目被添加到集合时,计数必须增加1,参数化单元测试说明如何将“n”项添加到集合中。然后Pex将尝试创建一个证明合同被破坏的单元测试。有关概述,请参阅此video

如果这有效,您的单元测试只需要为您尝试测试的每件事物的一个示例编写,然后PEX将能够计算出数据项将破坏测试

答案 5 :(得分:0)

我前段时间对这个话题进行了一些反思。

您可能需要查看

http://gleichmann.wordpress.com/2007/12/09/test-driven-development-and-design-by-contract-friend-or-foe/

答案 6 :(得分:0)

当您使用TDD实现新方法时,您需要一些输入:您需要知道要在您的测试中检查的断言。按合同设计给出了这些断言:它们是方法的后置条件和不变量。

答案 7 :(得分:0)

我发现DbC非常方便快速启动 red-green-refactor 循环,因为它有助于识别单元测试。使用DbC,我开始考虑前置条件,即TDD编辑的对象必须处理,并且每个前提条件可能代表启动红绿重构循环的失败单元测试。在某些时候,我切换到一个后置条件的失败单元测试开始循环,然后只是保持TDD流程。我已经尝试过这种方法与TDD的新手,它确实有助于启动TDD思维模式。

总之,将DbC视为识别关键行为单元测试的有效方法。 DbC有助于分析输入(前置条件)和输出(后置条件),这是我们需要控制(输入)和观察(输出)以编写可测试软件的两件事(TDD的类似目标)。 p>