【问题标题】:Design By Contract and Test-Driven Development [closed]按合同设计和测试驱动的开发[关闭]
【发布时间】:2008-12-27 02:48:06
【问题描述】:

我正在努力改进我们团队的开发流程,并且我正在考虑如何最好地通过测试驱动开发来实施按合同设计。这两种技术似乎有很多重叠之处,我想知道是否有人对以下(相关)问题有所了解:

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

这些问题的重点是这个更普遍的问题:如果我们已经在正确地进行 TDD,如果我们还使用 DbC,我们是否会从开销上获得显着收益?

一些细节,尽管我认为这个问题很大程度上与语言无关:

  • 我们的团队很小,
  • 我们主要使用 Perl。

【问题讨论】:

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


    【解决方案1】:

    注意区别。

    设计由合同驱动。合同驱动的设计

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

    它们是相关的,因为一个先于另一个。它们描述了不同抽象级别的软件。

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

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

    我看不到按合同设计的任何开销

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

    • 如果您不进行设计,那么编写合同将消除问题,降低成本和复杂性。

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

    1. 从合同开始,

    2. 然后

      一个。编写测试和

      b.写代码。

    看看这两个开发活动是如何从本质上交织在一起的,并且都来自合同。

    【讨论】:

    • +1。优秀的答案。假设您没有架构师或负责人来做一些原型设计,那么高级开发人员的初始合同开发也是一项很好的活动,可以将其作为工件移交给初级和中级开发人员。
    • 很好的答案,这正是我想要的。感谢您如此直接和简洁地解释两者之间的区别(和关系)。
    • 对于TDD,不应该是“a.写测试,b.写代码”吗?
    • @regu.pattabi:我再清楚不过了:“1. 从合约开始,然后 a. 编写测试,b. 编写代码。”
    • @Dran Dane:放松。编写一些代码,然后编写一些测试,然后编写其余的工作代码是可以的。不要认为“合同”和“代码”是完全不同的人,以至于整个过程永远无法正常工作。如果有帮助,您可以在测试之前编写接口。您应该避免先编写整个实现。有时,对于琐碎的代码,您最终会先编写所有代码。没关系。只是避免在测试之前编写大型实现。
    【解决方案2】:

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

    让我解释一下。

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

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

    第一部分可以部分替换为契约,至少在单元测试中是这样。 TDD 测试作为行为规范,对其他开发人员(单元测试)和领域专家(验收测试)都是如此。合同为其他开发人员、领域专家以及编译器和运行时库指定行为。

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

    还有一个 DbC 没有涵盖的 TDD 的重要部分:中间 D。在 TDD 中,测试驱动你的开发过程:你永远不会写一行实现代码,除非你有一个失败的测试,你永远不会写一行测试代码,除非你的测试全部通过,你只写最少量的实现代码来使测试通过,你只写最少量的测试代码来产生一个失败的测试。

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

    类似这样的:

    1. 为功能编写验收测试
    2. 为实现部分功能的单元编写单元测试
    3. 使用您在步骤 2 中设计的方法签名,编写方法原型
    4. 添加后置条件
    5. 添加前置条件
    6. 实现方法体
    7. 如果验收测试通过,则转到 1,否则转到 2

    如果您想知道按合同设计的发明者 Bertrand Meyer 对结合 TDD 和 DbC 的看法,他的小组有一篇不错的论文,名为 Contract-Driven Design = Test-Driven Development - Writing Test Cases。基本前提是合约提供所有可能用例的抽象表示,而测试用例只测试特定用例。因此,可以从合约中自动生成合适的测试工具。

    【讨论】:

      【解决方案3】:

      我会补充:

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

      我从一个艰难的过程中吸取了这个教训,一个大型项目走入了永无止境的地方,然后在严重的情况下以正确的方式应用它,公司生存,短导火线时间线。我们定义了协议,为交易的每一方定义并编写了一组协议仿真(基本上是罐装消息生成器和接收消息检查器,一个晚上的两脑编码),然后分开分别编写服务器端和客户端的应用程序。我们重新组合了演出之夜,而且效果很好。需求、设计、合同、测试、代码、集成。以该顺序。重复直到烘烤。

      我对 TLA 的设计有点怀疑。与模式一样,符合流行语的食谱是一个很好的指南,但根据我的经验,没有一刀切的设计或项目管理程序。如果您是按书 (tm) 精确地做事,那么除非它是具有国防部程序要求的国防部合同,否则您可能会在此过程中的某个地方遇到麻烦。阅读书籍,是的,但一定要了解它们,然后还要考虑团队中的人员方面。仅由 Book 强制执行的规则不会被统一执行 - 即使在工具强制执行时也可能会出现退出(例如 svn cmets 留空或神秘简短)。只有当工具链不仅强制执行程序,而且比任何可能的捷径更容易遵循程序时,程序才会被遵循。相信我,当事情变得艰难时,就会找到捷径,而您可能不知道凌晨 3 点使用的捷径,直到为时已晚。

      【讨论】:

        【解决方案4】:

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

        我会推荐使用 Ruby Cucumber http://github.com/aslakhellesoy/cucumber

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

        【讨论】:

          【解决方案5】:

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

          如果这可行,您的单元测试只需为您尝试测试的每一件事编写一个示例,然后 PEX 将能够计算出女巫数据项会破坏测试。

          【讨论】:

            【解决方案6】:

            前段时间我对这个话题有过一些思考。

            你可能想看看

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

            【讨论】:

            【解决方案7】:

            当您使用 TDD 实现新方法时,您需要一些输入:您需要知道要检查测试的断言。 Design-by-contract 为您提供了这些断言:它们是方法的后置条件和不变量。

            【讨论】:

              【解决方案8】:

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

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

              【讨论】:

                猜你喜欢
                • 2014-04-29
                • 1970-01-01
                • 2010-11-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-10-03
                • 1970-01-01
                相关资源
                最近更新 更多