- 原文地址:Outside-In development with Double Loop TDD
- 原文做者:Emily Bache
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:Yong Li
- 校对者:Liao Malin
在我上一篇 文章 中,我开始讨论伦敦派测试驱动开发(TDD),以及我认为它和传统 TDD 不一样的两个特色。第一个是利用双环 TDD 进行由外向内的开发,我将在这篇文章中详细讨论。第二点是「说,而不是问」的面向对象设计,我将在 下一篇文章 中再做讨论。前端
当你进行双环 TDD 时,你在内环上花费的时间是以分钟计的,而在外环上花费的时间是以小时或天计的。外环测试是从系统的外部用户的角度来写的,一般覆盖了粗粒度的功能,而且已部署在真实的(或至少接近真实的)环境中。在 个人书中 我把这类测试称之为「指导测试」(Guiding Test),而 Freeman 和 Pryce 称之为 「验收测试」(Acceptance Tests)。这些测试应当在客户指望不能知足时失败 —— 换而言之,它们提供了良好的回溯保护。它们也记载了系统应有的行为。(另见个人文章「敏捷自动化测试设计的原则」)android
我不认为双环 TDD 是伦敦派 TDD 特有的,我相信传统 TDD 开发者也会采用。这一理念早在 Kent Beck 的第一本关于极限编程的书中就存在了。但我认为伦敦派的独到之处在于由外向内的设计,而且辅之以 mock 的使用。ios
若是你使用双环 TDD,一般你会先写一个指导测试来体现一个用户是如何与你的系统交互的。这个测试会帮助你肯定位于最顶层,被首先调用的,做为需求功能的入口点的函数或类。这经常是一个 GUI 组件,一个网页上的连接,或是一个命令行标志。git
而对伦敦派 TDD 而言,等你开始设计那些由该 GUI 组件、网页连接或是命令行标志来调用的内环 TDD 的类或方法的时候,你很快就会意识到这些新的代码没法由本身来实现整块功能,而是须要其它的协做类来共同完成。github
用户观察系统,而且指望某些功能。这意味着系统的边界须要一个新的类。而这个类又进而须要更多还没有存在的协做类。编程
这些协做类尚不存在,或者至少不能提供你须要的所有功能。与其在此时暂停 TDD 而去马上开发这些新的类,你其实能够在测试中将它们替换为 mock。在你将接口和协议开发到知足需求以前,更换 mock 和实验代码一般是很容易的。如此一来,当你在设计测试用例的同时,你也在设计生产代码了。后端
你能够将协做对象替换为 mock,这样你就能设计它们之间的接口和协议了。架构
当你对你的设计满意了,而且测试也经过了之后,你就能够深刻下一层开始真正实现一个协做类。固然,若是某个类又进一步须要其它协做者,你能够再将它们替换为 mock 来进一步设计这些接口。这一方法能够持续整个系统设计,通达各个架构层和抽象层。ide
你已经完成了系统边界的类,如今你能够开发它的某个协做类,而且用 mock 替换这个类进一步须要的协做类。函数
这一工做方式可让你把问题分解成可控的部件,在你每开始一个新部件以前都能把当前的部件详细规定、充分测试。你能从关注用户需求开始,而后由外向内地构建,在系统中一个部件一个部件地追踪用户交互的全过程,直到指导测试能够经过。一般在指导测试中不会将系统的部件替换成 mock,这样最终当指导测试经过的时候你就能够确信你没有忘记实现任何一个协做类。
在传统 TDD 的方法中也能够由外向内,可是用一种几乎不须要 mock 的方法。存在几种不一样的策略来解决「协做类尚不存在」的问题。其中一种是从退化的用例开始设计,此时从用户视角来看几乎什么都没发生。这是一种当输出比实际用例,或者愉快路径要简单得多的时候的特例。这样你就能只用最基础的空实现,或者假的返回值来构建这一简化版的功能需求所须要的类的结构和方法。一旦结构有了,你就能够充实它(或许由内而外地进行也行)。
另一种在传统 TDD 中由外向内的策略是,先由外向内地写测试,而当你发现你没法在某个协做类被实现以前使测试经过之时,就注释掉那个测试,转而去实现所需的协做类。最终你会发现你能够仅凭已经存在的协做者,就彻底实现某个类,由此再逐步向上实现。
由外向内有时在传统 TDD 方法中也许根本行不通。你会从系统中心的某个类开始,挑出某个仅凭已有的协做者就能彻底实现和测试的部件。这一般是应用的领域模型的中心的一个类。当它完成之后,你再由中心向外继续开发系统,一个一个地添加新的类。由于只使用已有的类,你就几乎不须要使用 mock。最终你也会发现你完成了全部功能,也经过了指导测试。
我认为由外向内的方法是有显著优点的。它能帮助你持续关注用户的真正所需,使你构建一些真正有用的东西,而避免浪费时间粉饰打磨用户不须要的。我认为不管对传统 TDD 仍是伦敦派 TDD 来讲,由外向内的方法都须要技巧和训练。学会如何将功能拆解成你能一步一步来开发和设计的增量部件并不是易事。可是若是你由中心向外工做,就存在你会构建用户不须要的东西的风险,或者当你抵达外层却最终发现系统并不适用,而不得不进行重构。
然而,假设你已是由外向内工做的了,我仍认为,取决于你是在真正的生产代码中编写假的实现,仍是只在 mock 中写,这二者是有所不一样的。若是你在生产代码中写,你就逐步须要把它们取代为真正的功能。而若是你把假的功能放在 mock 中,它们就能永远存在于测试代码中,即便当真的功能已经实现了,它们还在那儿。这对于程序文档颇有用,也能让你的测试得以继续快速执行。
话虽如此,也存在一些关于在测试中使用了不少 mock 以后的可维护性的争议。当设计更改时,除了生产代码外还要更新全部的 mock 也许代价太大了。一旦真正的实现完成了,或许内环测试就应该被删除?毕竟指导测试已经能提供你须要的所有回溯保护了,所以那些仅仅对你最初的设计有用的测试并不值得保留?我不以为这样作就是毫无指摘的。从我和一些伦敦派支持者的讨论来看,即便他们也会删除部分的测试,但他们并不会删除全部使用了 mock 的测试。
我也仍在尝试理解这些争端,而且试着找出在怎样的场合里伦敦派 TDD 能够带来最大的收益。我但愿我已经概述了由外向内的开发中,各类方法的区别。在个人下篇文章中,我将探讨伦敦派 TDD 是如何推广「说,而不是问」的面向对象设计 的。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。