这些年来,我喜欢用下面这三条简单的规则来描述测试驱动开发:
- 除非这能让失败的单元测试通过,否则不允许去编写任何的产品代码。
- 只允许编写刚好能够导致失败的单元测试。 (编译失败也属于一种失败)
- 只允许编写刚好能够导致一个失败的单元测试通过的产品代码。
对于任何功能,一定要从编写它的单元测试开始;但是到了原则2,你就不能再为那个单元测试写更多内容。只要一出现该单元测试代码编译失败,或是断言失败,你就必须停下来开始编写产品代码;但是到了原则3,你就只能编写产品代码,直到让测试编译成功或通过断言为准。
仔细想想,就会发现如果不是去让一些东西编译或是执行,你就根本没办法去写代码。确实,这也正是关键所在。我们所做的任何事情(无论是写测试,写产品代码,或是重构),都要保持系统能够一直运行。跑通测试的时间间隔应该是以秒或是分钟级的,即使那只有10分钟,也都太长了。
想了解实际的操作过程,可以看看“保龄球游戏中的Kata”(译注2)一文。
现今有很多程序员,每当他们头一次听到这种技术的时候,会想:“这种做法太愚蠢了!”“这会让我慢下来,这是时间和精力的浪费,让我无法思考,无法设计,还会打乱我的思路。”然而,试想,当你走进一个房间,里面都是用这种方式工作的人们,会发生什么情况?你随便选个时间,随意找个人,一分钟以前,他们所有的代码都跑通过。
让我再重复一遍:一分钟以前每个人的代码都能跑通!不管你找谁,也不管你何时去找,一分钟以前他们所有的代码都跑通了!
如果你所有的代码自始至终都能跑通,那么你会多长时间用一次调试器?答案是,不会太经常。只要敲几下^Z就能很容易的恢复这些代码到跑通时的状态,然后再试着把前几分钟的代码写一遍即可。而如果你不经常调试,那么会省去多少时间呢?而现在你花在调试上的时间又有多少呢?一旦你有调试过,你又要花上多少时间来修复这些bug呢?如果你能够大大减少这些时间的话,又会如何呢?
好处还不只这些。如果你采用这种方法,那么每小时你都会产生好几个测试;每天就有十几个;每个月就几百个;一年下来,你所编写的测试就会有数千个。你可以保留着这些测试而且在任何你希望的时候去运行它们!什么时候运行它们呢?随时!只要你做了改动,就去运行它们!
有些代码已经混乱不堪了,可是我们为何不去清理它们呢?我们担心会破坏它们。但如果我们拥有测试,我们就有理由确信这些代码不会被破坏,或是说我们可以很快的就找到被破坏的地方。如果我们有了这些测试,我们就对代码发生改变无所忌惮。如果我们看到混乱的代码,或是一个不清晰的结构,可以毫无顾虑地清理它。因为有了测试,代码重新变得易修改了;因为有了测试,软件重新变“软”了。
好处还不只这些。如果你想要知道如何去调用一个特定的API,就会有一个测试能够告诉你;如果你想要知道如何创建一个特定的对象,就会有个测试能告诉你。你想知道的任何有关这个系统的,都有一个测试能够去示意。这些测试就像是小型的设计文档,小型的代码示例,描述了系统的工作和使用方式。
你是否曾经整合过一个第三方库到你的项目中呢?你拿到一本厚厚的精致的帮助文档,在结尾处,有一沓薄薄的示例附录。你会选择哪个去阅读呢?当然是示例了!那就是单元测试啊!它们是这份文档中最有用的部分;它们是如何使用代码的鲜活的例子;它们是极其详细、完全没有歧义的设计文档,相当的正规甚至可以执行,而且不会与产品代码相脱离。
好处还不只这些。如果你曾有过增加单元测试到一个可以工作的系统中的经历,你会发现那一点儿都不好玩。你很可能会发现想跑通测试,你一定是要么去改变系统中的部分设计,要么就在测试上作假。这是因为你试图去测试的系统并不是基于可测试设计的。例如,你想要测试某个函数f。然而,f调用了另外一个从数据库中删除记录的函数。在你的测试中,你不希望这条记录被删掉,但却没有办法来阻止它。这样的系统就不是基于可测试设计的系统。
当你遵循TDD的三条规则的时候,你的所有代码天生就可测试!而且另一个能形容“可测试”的词汇就是“可解耦”。为了单独的测试一个模块,你就必须把它解耦。所以TDD强制你去解耦这些模块。确实,如果你遵循这三条规则的话,你会发现你可能比起从前来能做出更多的解藕。这就强制了你去创造更好的,低耦合的设计。
收获了所有的这些好处,这些愚蠢的小规则实际上好像就没那么愚蠢了吧。他们实际上很可能是一些基本而又深刻的原则。确实,在我接触TDD之前我曾做过将近30年的程序员,我不认为曾有过什么人教过我什么非常奏效但又基本的编程实践。毕竟三十年意味着很多的经验。但当我开始了使用TDD,我就被这项技术的有效性所震撼,也沉迷其中。我不会再考虑去敲一大堆代码然后平白指望它们能运行成功。对于那种先将一组模块打散,然后希望能够再将它们整合起来,并且让它们在能下个礼拜五前运行起来的做法,我也不再能忍受。在我写程序的时候,每一个决定都由这种让现在开始写的代码能在一分钟后再次执行这样的基本需求所驱使着。
译注:
1,TDD,测试驱动开发。
2,Kata,可理解为武功招式,详细解释可参见译者的上篇译文。
小注:本篇文章由邓辉先生亲自校正,并给予了很多中肯而深刻的建议,译者在此给予感谢!
(原文链接网址:http://www.butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd; Robert C. Martin的英文blog网址:http://www.butunclebob.com/ArticleS.UncleBob)
译者注:Robert C. Martin是Object Mentor公司总裁,面向对象设计、模式、UML、敏捷方法学和极限编程领域内的资深顾问。他不仅是Jolt获奖图书《敏捷软件开发:原则、模式与实践》(中文版)(《敏捷软件开发》(英文影印版))的作者,还是畅销书Designing Object-Oriented C++ Applications Using the Booch Method的作者。Martin是Pattern Languages of Program Design 3和More C++ Gems的主编,并与James Newkirk合著了XP in Practice。他是国际程序员大会上著名的发言人,并在C++ Report杂志担任过4年的编辑。
分享到:
相关推荐
java测试源码 tdd-java-ch02-example-junit Reference:
标题 "tdd-java-ch09-feature-toggle:Java测试驱动开发第9章" 指向的是一个关于使用Java进行测试驱动开发(TDD)的项目,特别关注的是“功能切换”(feature toggle)这一概念。在软件开发中,功能切换是一种策略,...
**测试驱动开发(TDD)概述** 测试驱动开发(Test-Driven Development,简称TDD)是一种软件开发方法,强调在编写任何实际代码之前先编写测试用例。这种方法由Kent Beck在其2003年的著作《Test-Driven Development:...
### TDD-LTE信令详解知识点 #### 一、概述 TDD-LTE(Time Division Duplexing - Long Term Evolution)是一种基于时分双工技术的4G移动通信标准,其核心在于支持灵活的频谱分配,适用于非对称业务需求。在TDD-LTE...
【运维文档_eRAN3.0_LTETDD问题定位指导书-寻呼篇-20121025-A-1.2.pdf】是一份华为技术有限公司内部使用的运维文档,详细介绍了LTE TDD(时分双工)系统中关于寻呼问题的定位和解决策略。这份文档适用于DNB6200 TDS-...
kata-tdd-1-BINH-NGUYEN-VAN kata 首先,我们必须安装grunt插件以帮助运行此演示: 1.打开命令行 2.转到目录:“ kata-tdd-1-BINH-NGUYEN-VAN” 3.键入命令:npm install 4.运行命令:grunt serve 问题 Q1:在...
tdd-java-ch07-books-store Reference 测试步骤 启动 Books Store ./run_books_store.sh 启动测试 需要安装 FirefoxDriver,每次测试前需重启 Books Store gradle clean test 关闭 Books Store docker rm -f books_...
LTE-TDD随机接入过程(3)-RAR(MSG2)以及MSG1的重传.pdf
java源码测试 tdd-java-ch06-tic-tac-toe-mongo-my-version Reference: 测试步骤 启动 MongoDB ./run_mongoDB.sh 启动测试 gradle clean test 关闭 MongoDB docker rm -f mongoDB >/dev/null 2>&1
【标题】"kata-tdd-1-Tran-Anh-Tai:tdd 样本"是一个关于测试驱动开发(Test-Driven Development, TDD)的实践项目,由 Tran Anh Tai 提供。这个项目旨在帮助开发者熟悉 TDD 的过程和方法,并通过实际操作来提升其在 ...
使用Tape进行测试驱动开发(TDD)的初学者指南。 注意:如果您不熟悉测试驱动开发(TDD),我们将提供有关测试的更一般的初学者介绍和背景知识: : :grinning_squinting_face: H44 TDD 一个叉子: : 通过...
TDD-LTE信令流程与分析指导 本文档提供了TDD-LTE信令流程的详细介绍和分析指导,旨在帮助读者了解LTE信令流程、掌握接入流程、寻呼和切换等典型信令流程,并具备简单的问题分析能力。 第1章 开机入网流程介绍分析 ...
标题 "ses-tdd-exercise-1-template-源码.rar" 提示我们这是一个关于软件开发的练习项目,可能涉及测试驱动开发(TDD)的概念。在这个练习中,"template" 指的可能是一个模板项目,用于指导开发者进行TDD实践。源码...
【标题】"tdd-java-ch03-tic-tac-toe:Java测试驱动开发第3章" 涉及的主要知识点是测试驱动开发(Test-Driven Development,简称TDD)在Java编程中的应用,通过一个具体的例子——井字游戏(Tic-Tac-Toe)来阐述。...
kata-tdd-1-Luong-Thanh-Danh 卡塔 设置环境: 安装 Node.js 在 安装 Karma,一个测试运行器 推荐的方法是在项目目录中本地安装 Karma(以及项目需要的所有插件)。 安装业力: $ npm install karma --save-dev...
### 测试驱动开发(TDD)在Django与...以上内容涵盖了本书《tdd-for-web-development-with-django-and-selenium》的部分关键知识点,这些知识点不仅对学习Django和Selenium有帮助,同时也适用于更广泛的Web开发领域。
TDD-LTE(时分双工LTE)是LTE的一个变种,它利用时间分复用的方式实现上行链路和下行链路的数据传输。本培训教程将深入探讨TDD-LTE的关键概念和技术细节,帮助读者全面理解这一领域的核心知识。** 1. **LTE概述** ...
**LTE-TDD与LTE-FDD的区别** 随着移动通信技术的快速发展,无线通信系统正在逐步迈向更高的数据速率、更短的延迟以及更低的成本。LTE(Long Term Evolution)作为3G向4G演进的关键技术,有两种主要的双工模式:频分...
### 测试驱动开发 (TDD) 知识点解析 #### 一、TDD概念与原理 **测试驱动开发(Test-Driven Development, TDD)** 是一种软件开发方法论,其核心思想是在编写功能代码之前先编写测试用例。这种方法能够确保软件的...
tdd-book-examples-using-spock TDD 书籍(Kent Beck 着)使用 Spock 的示例这旨在作为使用 Spock 的示例的后续指南。开始从最开始的结帐标签“ch01”开始: git checkout tags/ch01跳跃前进要查看完整的标签列表: ...