我认为 DbC 和 TDD 之间存在重叠,但是,我认为没有重复的工作:引入 DbC 可能会导致测试用例的减少。
让我解释。
在 TDD 中,测试并不是真正的测试。它们是行为规范。但是,它们也是设计工具:通过首先编写测试,您可以像用户一样使用被测对象的外部 API(您实际上还没有编写)。这样,您以一种对用户有意义的方式设计 API,而不是使您最容易实现的方式。类似的东西,queue.full?
而不是queue.num_entries == queue.size
.
第二部分不能被合同取代。
第一部分可以被合约部分替代,至少在单元测试中是这样。TDD 测试作为行为规范,对其他开发人员(单元测试)和领域专家(验收测试)都是如此。契约还为其他开发人员、领域专家以及编译器和运行时库指定行为。
但是契约有固定的粒度:你有方法的前置条件和后置条件、对象不变量、模块契约等等。也许循环变体和不变量。然而,单元测试是测试行为单元。这些可能小于一个方法或由多个方法组成。这不是你可以用合同做的事情。对于“大图”,您仍然需要集成测试、功能测试和验收测试。
DbC 没有涵盖 TDD 的另一个重要部分:中间 D。在 TDD 中,测试驱动您的开发过程:除非您的测试失败,否则您永远不会编写一行实现代码,您永远不会编写一行代码测试代码 除非您的测试全部通过,否则您只需编写最少数量的实现代码即可使测试通过,您只需编写最少数量的测试代码即可产生失败的测试。
总之:使用测试来设计 API 的“流程”、“感觉”。使用合约来设计 API 的合约。使用测试为开发过程提供“节奏”。
像这样的东西:
- 为功能编写验收测试
- 为实现该功能的一部分的单元编写单元测试
- 使用您在步骤 2 中设计的方法签名,编写方法原型
- 添加后置条件
- 添加前置条件
- 实现方法体
- 如果验收测试通过,则转到 1,否则转到 2
如果你想知道按合同设计的发明者 Bertrand Meyer 对结合 TDD 和 DbC 的看法,他的小组有一篇很好的论文,叫做Contract-Driven Design = Test-Driven Development - Writing Test Cases。基本前提是合约提供了所有可能用例的抽象表示,而测试用例只测试特定用例。因此,可以从合约中自动生成合适的测试工具。