﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-XZC.Log-随笔分类-软件测试</title><link>http://www.blogjava.net/xzclog/category/17321.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 03:19:47 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 03:19:47 GMT</pubDate><ttl>60</ttl><item><title>模糊测试 对代码质量影响深远的技术 </title><link>http://www.blogjava.net/xzclog/archive/2006/11/08/79824.html</link><dc:creator>xzc</dc:creator><author>xzc</author><pubDate>Wed, 08 Nov 2006 04:49:00 GMT</pubDate><guid>http://www.blogjava.net/xzclog/archive/2006/11/08/79824.html</guid><wfw:comment>http://www.blogjava.net/xzclog/comments/79824.html</wfw:comment><comments>http://www.blogjava.net/xzclog/archive/2006/11/08/79824.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xzclog/comments/commentRss/79824.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xzclog/services/trackbacks/79824.html</trackback:ping><description><![CDATA[ 模糊测试（Fuzz testing ）是一项对代码质量有着深远影响的简单技术。在本文中，Elliotte Rusty Harold 故意将随机的坏数据插入应用程序，以观察发生的结果。他也解释了如何使用如校验和、XML 数据存储及代码验证等防护性编码技术，来加固您的程序以抵制随机数据。他以一个练习进行总结，在练习中他以一个代码破坏者的角度进行思考 —— 这是一种用于防护代码的至关重要的技术。<!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--END RESERVED FOR FUTURE USE INCLUDE FILES--><p>     多年来，我惊叹于有如此大量能够使 Microsoft Word 崩溃的坏文件。少数字节错位，会使整个应用程序毁于一旦。在旧式的、无内存保护的操作系统中，整个计算机通常就这样宕掉了。Word 为什么不能意识到它接收到了坏的数据，并发出一条错误信息呢？为什么它会仅仅因为少数字节被损坏就破坏自己的栈、堆呢？当然，Word 并不是惟一一个面对畸形文件时表现得如此糟糕的程序。</p><p>    本文介绍了一种试图避免这种灾难的技术。在模糊测试中，用随机坏数据（也称做 fuzz）攻击一个程序，然后等着观察哪里遭到了破坏。模糊测试的技巧在于，它是不符合逻辑的：自动模糊测试不去猜测哪个数据会导致破坏（就像人工测试员那样），而是将尽可能多的杂乱数据投入程序中。由这个测试验证过的失败模式通常对程序员来说是个彻底的震憾，因为任何按逻辑思考的人都不会想到这种失败。</p><p>    模糊测试是一项简单的技术，但它却能揭示出程序中的重要 bug。它能够验证出现实世界中的错误模式并在您的软件发货前对潜在的应当被堵塞的攻击渠道进行提示。</p><p><span class="atitle" twffan="done"><strong>模糊测试如何运行</strong></span></p><p>模糊测试的实现是一个非常简单的过程：</p><ol><li>准备一份插入程序中的正确的文件。 
</li><li>用随机数据替换该文件的某些部分。 
</li><li>用程序打开文件。 
</li><li>观察破坏了什么。 </li></ol><p>    可以用任意多种方式改变该随机数据。例如，可以将整个文件打乱，而不是仅替换其中的一部分，也可以将该文件限制为 ASCII 文本或非零字节。不管用什么方式进行分割，关键是将大量随机数据放入应用程序并观察出故障的是什么。</p><table style="FONT-SIZE: 12px" cellspacing="0" cellpadding="0" width="40%" align="right" border="0"><tbody><tr><td width="10"><img height="1" alt="" src="http://java.chinaitlab.com/UploadFiles_8734/200611/20061107095056862.gif" width="10" twffan="done" /></td><td><table style="FONT-SIZE: 12px" cellspacing="0" cellpadding="5" width="100%" border="1"><tbody><tr><td bgcolor="#eeeeee">测试基于 C 的应用程序<br />当字符串包含额外的零时，许多用 C 编写的程序都会出问题 —— 这类问题太过频繁以至于额外的零能够彻底隐藏代码中其他的问题。一旦验证出程序存在零字节问题，就可以移除它们，从而让其他的问题浮现出来。 </td></tr></tbody></table></td></tr></tbody></table><p>    可以手动进行初始化测试，但要想达到最佳的效果则确实需要采用自动化模糊测试。在这种情况下，当面临破坏输入时首先需要为应用程序定义适当的错误行为。（如果当输入数据被破坏时，您发现程序正常运行，且未定义发生的事件，那么这就是第一个 bug。）随后将随机数据传递到程序中直到找到了一个文件，该文件不会触发适当的错误对话框、消息、异常，等等。存储并记录该文件，这样就能在稍后重现该问题。如此重复。</p><p>    尽管模糊测试通常需要一些手动编码，但还有一些工具能提供帮助。例如，清单 1 显示了一个简单的 Java™ 类，该类随机更改文件的特定长度。我常愿意在开始的几个字节后面启动模糊测试，因为程序似乎更可能注意到早期的错误而不是后面的错误。（您的目的是想找到程序未检测到的错误，而不是寻找已经检测到的。）</p><br />清单 1. 用随机数据替换文件部分的类<br /><table style="FONT-SIZE: 12px" cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td class="code-outline"><pre class="displaycode">import java.io.*;
import java.security.SecureRandom;
import java.util.Random;
public class Fuzzer {
     private Random random = new SecureRandom();
     private int count = 1;
     public File fuzz(File in, int start, int length) throws IOException  
{
         byte[] data = new byte[(int) in.length()];
         DataInputStream din = new DataInputStream(new FileInputStream(in));
         din.readFully(data);
         fuzz(data, start, length);
         String name = "fuzz_" + count + "_" + in.getName();
         File fout = new File(name);
         FileOutputStream  out = new FileOutputStream(fout);
         out.write(data);
         out.close();
         din.close();
         count++;
         return fout;
     }
     // Modifies byte array in place
     public void fuzz(byte[] in, int start, int length) {
         byte[] fuzz = new byte[length];
         random.nextBytes(fuzz);
         System.arraycopy(fuzz, 0, in, start, fuzz.length);
     }
            }</pre></td></tr></tbody></table><br /><table style="FONT-SIZE: 12px" cellspacing="0" cellpadding="0" width="40%" align="right" border="0"><tbody><tr><td width="10"><img height="1" alt="" src="http://java.chinaitlab.com/UploadFiles_8734/200611/20061107095056862.gif" width="10" twffan="done" /></td><td><table style="FONT-SIZE: 12px" cellspacing="0" cellpadding="5" width="100%" border="1"><tbody><tr><td bgcolor="#eeeeee">关于代码<br />我可以用很多种方式优化 清单 1 中的代码。例如，有着 java.nio 的内存映射文件是一个相当不错的选择。我也能够改进这个错误处理及可配置性。因为不想让这些细节混淆这里所要说明的观点，所以我将代码保持了原样。 </td></tr></tbody></table></td></tr></tbody></table><p>    模糊测试文件很简单。将其传至应用程序通常不那么困难。如 AppleScript 或 Perl 脚本语言通常是编写模糊测试的最佳选择。对于 GUI 程序，最困难的部分是辨认出应用程序是否检测出正确的故障模式。有时，最简单的方法是让一个人坐在程序前将每一个测试通过或失败的结果都标记下来。一定要将所有生成的随机测试用例单独地命名并保存下来，这样就能够重现这个过程中检测到的任何故障。</p><p><span class="atitle" twffan="done"><strong>防护性编码</strong></span></p><p>    可靠的编码遵循了这样的基本原则：绝不会让程序中插入未经过一致性及合理性验证的外部数据。</p><p>    如果从文件中读入一个数字并期望其为正数，那么，在使用其进行进一步处理前对其先验证一下。如果期望字符串只包含 ASCII 字母，请确定它确实是这样。如果认为文件包含一个四字节的整数倍的数据，请验证一下。一定不要假设任何外部提供的数据中的字符都会如您所料。</p><p>    最常见的错误是做出这样的假设：因为程序将该数据写出，该程序就能不用验证再一次将该数据读回去。这是很危险的！因为该数据很可能已经被另一个程序在磁盘上复写过了。它也可能已经被一个故障磁盘或坏的网络传输所破坏了或已经被另一个带 bug 的程序更改过了。它甚至可能已经被故意更改过以破坏程序的安全性。所以不要假设任何事，要进行验证。</p><p>    当然，错误处理及验证十分令人生厌，也很不方便，并被全世界程序员们所轻视。计算机的诞生已进入了六十个年头，我们仍旧没有检查基本的东西，如成功打开一个文件及内存分配是否成功。让程序员们在阅读一个文件时测试每一个字节和每一个不变量似乎是无望的 —— 但不这样做就会使程序易被模糊攻击。幸运的是，可以寻求帮助。恰当使用现代工具和技术能够显著减轻加固应用程序的痛苦，特别是如下三种技术更为突出：</p><ul><li>校验和 
</li><li>基于语法的格式，如 XML 
</li><li>验证过的代码如 Java </li></ul><span class="smalltitle" twffan="done"><strong><p><span class="smalltitle" twffan="done"><strong>用校验和进行的模糊试验</strong></span></p><p>    能够保护程序抵御模糊攻击的最简单的方法是将一个检验和添加到数据中。例如，可以将文件中所有的字节都累加起来，然后取其除以 256 的余数。将得到的值存储到文件尾部的一个额外字节中。然后，在输入数据前，先验证检验和是否匹配。这项简单模式将未被发现的意外故障的风险降低到约 1/256 。</p><p>    健壮的校验和算法如 MD5 和 SHA 并不仅仅取其除以 256 的余数，它完成的要多得多。在 Java 语言中，java.security.DigestInputStream 和 java.security.DigestOutputStream 类为将一个校验和附属到数据中提供了便捷的方式。使用这些校验和算法中的一种可以将程序遭受意外破坏的机率降低到少于十亿分之一（尽管故意攻击仍有可能）。</p><p><span class="smalltitle" twffan="done"><strong>XML 存储及验证</strong></span></p><p>   将数据以 XML 形式存储是一种避免数据损坏的好方法。XML 最初即着力于 Web 页面、书籍、诗歌、文章及相似文档，它几乎在每个领域都获取了巨大的成功，从金融数据到矢量图形到序列化对象等等。</p><p></p><table style="FONT-SIZE: 12px" cellspacing="0" cellpadding="0" width="40%" align="right" border="0"><tbody><tr><td width="10"><img height="1" alt="" src="http://java.chinaitlab.com/UploadFiles_8734/200611/20061107095056862.gif" width="10" twffan="done" /></td><td><table style="FONT-SIZE: 12px" cellspacing="0" cellpadding="5" width="100%" border="1"><tbody><tr><td bgcolor="#eeeeee">不切实际的限定<br />如果真想要破坏一个 XML 解析器，有几种方法可以试试。例如，大多数 XML 解析器服从于特定的最大尺寸。如果一个元素名长度超过 22 亿字符（Java String 的最大尺寸），SAX 解析器将会失败。尽管如此，在实践中这些极限值如此之高，以至于在达到之前内存就已经耗尽。 </td></tr></tbody></table></td></tr></tbody></table><p>    使 XML 格式抵制模糊攻击的关键特征是一个对输入不做任何 假设的解析器。这就是真正想在一个健壮的文件格式中所获得的。设计 XML 解析器是为了让任何输入（格式良好的或无格式的，有效的或无效的）都以定义好的形式处理。XML 解析器能够处理任何 字节流。如果数据首先通过了 XML 解析器，则仅需要准备好接受解析器所能提供的东西。例如，不需要检查数据是否包含空字符，因为 XML 解析器绝不会传送一个空值。如果 XML 解析器在其输入中看到一个空字符，它就会发出异常并停止处理。当然还需要处理这个异常，但编写一个 catch 块来处理检测到的错误比起编写代码来检测所有可能的错误来说要简单得多。</p><p>    为使程序更加安全，可以用 DTD 和/或模式来验证文档。这不仅检查了 XML 是否格式良好，而且至少与所预期更加接近。验证并不会告知关于文档所需了解的一切，但它却使编写大量简单检查变得很简单。用 XML，很明显能够将所接受的文档严格地限定为能够处理的格式。</p><p>    尽管如此，还有多段代码不能用 DTD 或模式进行验证。例如，不能测试发票上商品的价格是否和数据库中库存商品的价格一致。当从客户接收到一份包含价格的订单文档时，不论其是 XML 格式或是其他格式，在提交前通常都会检查一下，以确保客户并未修改价格。可以用定制代码实现这些最后的检查。</p><p><span class="smalltitle" twffan="done"><strong>基于语法的格式</strong></span></p><p>    使 XML 能够对模糊攻击具有如此的抵御能力的是其使用巴科斯-诺尔范式（Backus-Naur Form，BNF）语法仔细且标准地定义的格式。许多解析器都是使用如 JavaCC 或 Bison 等解析器-生成器工具直接从此语法中构建的。这种工具的实质是阅读一个任意的输入流并确定其是否符合此语法。</p><p>    如果 XML 并不适合于您的文件格式，您仍可以从基于解析器的解决方案的健壮性中获益。您必须为文件格式自行编写语法，随后开发自己的解析器来阅读它。相比使用唾手可得的 XML 解析器，开发自己的解析器需要更多的工作。然而它是一个更为健壮的解决方案，而不是不根据语法正式地进行验证就将数据简单地装载到内存中。</p><p><span class="smalltitle" twffan="done"><strong>Java 代码验证</strong></span></p><p>    由模糊测试导致的许多故障都是内存分配错误及缓冲器溢出的结果。用一种安全的垃圾收集语言（在如 Java 或 managed C# 等虚拟机上执行的）来编写应用程序避免了许多潜在问题。即使用 C 或 C++ 来编写代码，还是需要使用一个可靠的垃圾收集库。在 2006 年，台式机程序员或服务器程序员不应该还需要管理内存。</p><p>    Java 运行时对其自身的代码起到了额外保护层的作用。在将一个 .class 文件装载到虚拟机之前，该文件要由一个字节符验证器或一个可选的 SecurityManager 进行验证。Java 并不假设创建 .class 文件的编译器没有 bug 且运转正常。设计 Java 语言之初就是为了允许在一个安全沙箱中运行不信任的、潜在恶意的代码。它甚至不信任其自身编译过的代码。毕竟，也许有人已经用十六进制编辑器手工修改了字节符，试图触发缓冲器溢出。我们大家都应该对我们的程序也有对输入这样的偏执。</p><p><span class="atitle" twffan="done"><strong>以敌人的角度思考</strong></span></p><p>    之前介绍的每项技术都在阻止意外破坏方面造诣颇深。将它们综合起来恰当地实现，会将未被发现的非蓄意破坏发生的可能性几乎减少到零。（当然，并不会减少到零，但其发生的可能性就如同一束偏离轨道的宇宙射线将您 CPU 运算 1+1 的结果变为 3 的可能性一样微乎其微。）但不是所有的数据损坏都是非蓄意的。如果有人故意引入坏数据来破坏程序的安全性又该如何呢？以一个攻击者的角度进行思考是防护代码的下一个步骤。</p><p>     转回到一个攻击者的角度进行思考，假设要攻击的应用程序是用 Java 编程语言编写的、使用非本地代码且将所有额外数据都以 XML（在接受前经过彻底验证）形式存储，还能成功攻击吗？是的，能。但用随机改变文件字节的低级方法显然不行。需要一种更为复杂的方法来说明程序自身的错误检测机制及路径。</p><p>    当测试一个抵御模糊攻击的应用程序时，不可能做纯黑盒测试，但通过一些明显的修改，基本的想法还是可以应用的。例如，考虑校验和，如果文件格式包含一个校验和，在将文件传至应用程序前仅仅修改此校验和就可以使其同随机数据相匹配。</p><p>    对于 XML，试着模糊单独的元素内容和属性值，而不是从文档中挑选一部分随机的字节进行替换。一定要用合法的 XML 字符替换数据，而不要用随机字节，因为即使一百字节的随机数据也一定是畸形的。也可以改变元素名称和属性名称，只要细心地确保得到的文档格式仍是正确的就可以了。如果该 XML 文档是由一个限制非常严格的模式进行检查的，还需要计算出该模式没有 检查什么，以决定在哪里进行有效的模糊。</p><p>    一个结合了对剩余数据进行代码级验证的真正严格的模式也许不会留下可操纵的空间。这就是作为一个开发人员所需要追求的。应用程序应能够处理所发送的任何有意义的字节流，而不会因权利上（ de jure ） 无效而拒绝。</p><p><span class="atitle" twffan="done"><strong>结束语</strong></span></p><p>    模糊测试能够说明 bug 在程序中的出现。并不证明不存在这样的 bug。而且，通过模糊测试会极大地提高您对应用程序的健壮性及抵御意外输入的安全性的自信心。如果您用 24 小时对程序进行模糊测试而其依然无事，那么随后同种类型的攻击就不大可能再危及到它。（并不是不可能，提醒您，只是可能性很小。）如果模糊测试揭示出程序中的 bug，就应该进行修正，而不是当 bug 随机出现时再对付它们。模糊测试通过明智地使用校验和、XML、垃圾收集和/或基于语法的文件格式，更有效地从根本上加固了文件格式。</p><p>    模糊测试是一项用于验证程序中真实错误的重要工具，也是所有意识到安全性问题且着力于程序健壮性的程序员们的工具箱中所必备的工具。</p><p><span class="atitle" twffan="done"><strong>关于作者</strong></span></p><p></p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td colspan="3"><img height="5" alt="" src="http://java.chinaitlab.com/UploadFiles_8734/200611/20061107095056862.gif" width="100%" twffan="done" /></td></tr><tr valign="top" align="left"><td><p><img height="80" alt="Elliot Rusty Harold ç…§ç‰‡" src="http://www.ibm.com/developerworks/i/p-eharold.jpg" width="64" align="left" twffan="done" valign="top" /></p></td><td><img height="5" alt="" src="http://java.chinaitlab.com/UploadFiles_8734/200611/20061107095056862.gif" width="4" twffan="done" /></td><td width="100%"><p>Elliotte Rusty Harold 来自新奥尔良， 现在他还定期回老家喝一碗美味的秋葵汤。不过目前，他和妻子 Beth 定居在纽约临近布鲁克林的 Prospect Heights，同住的还有他的猫咪 Charm（取自夸克）和 Marjorie（取自他岳母的名字）。他是 Polytechnic 大学计算机科学的副教授，他在该校讲授 Java 和面向对象编程。他的 Web 站点 Cafe au Lait 已经成为 Internet 上最流行的独立 Java 站点之一，它的姊妹站点 Cafe con Leche 已经成为最流行的 XML 站点之一。他的书包括 Effective XML、 Processing XML with Java、 Java Network Programming 和 The XML 1.1 Bible。他目前在从事处理 XML 的 XOM API、Jaxen XPath 引擎和 Jester 测试覆盖率工具的开发工作。</p></td></tr></tbody></table></strong></span><img src ="http://www.blogjava.net/xzclog/aggbug/79824.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xzclog/" target="_blank">xzc</a> 2006-11-08 12:49 <a href="http://www.blogjava.net/xzclog/archive/2006/11/08/79824.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试驱动开发全功略 </title><link>http://www.blogjava.net/xzclog/archive/2006/09/29/72761.html</link><dc:creator>xzc</dc:creator><author>xzc</author><pubDate>Fri, 29 Sep 2006 03:43:00 GMT</pubDate><guid>http://www.blogjava.net/xzclog/archive/2006/09/29/72761.html</guid><wfw:comment>http://www.blogjava.net/xzclog/comments/72761.html</wfw:comment><comments>http://www.blogjava.net/xzclog/archive/2006/09/29/72761.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xzclog/comments/commentRss/72761.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xzclog/services/trackbacks/72761.html</trackback:ping><description><![CDATA[
		<p>
				<font color="#ff0000" size="5">{关键字}</font>
		</p>
		<p>测试驱动开发/Test Driven Development/TDD<br />测试用例/TestCase/TC<br />设计/Design<br />重构/Refactoring</p>
		<p>
				<font color="#ff0000" size="5">{TDD的目标}</font>
		</p>
		<blockquote>
				<p>
						<font style="FONT-WEIGHT: bold; FONT-SIZE: large; BACKGROUND-COLOR: rgb(220,220,220)">Clean Code That Works</font>
				</p>
		</blockquote>
		<p>这句话的含义是，事实上我们只做两件事情：让代码奏效（Work）和让代码洁净（Clean），前者是把事情做对，后者是把事情做好。想想看，其实我们平时所做的所有工作，除去无用的工作和错误的工作以外，真正正确的工作，并且是真正有意义的工作，其实也就只有两大类：增加功能和提升设计，而TDD 正是在这个原则上产生的。如果您的工作并非我们想象的这样，（这意味着您还存在第三类正确有意义的工作，或者您所要做的根本和我们在说的是两回事），那么这告诉我们您并不需要TDD，或者不适用TDD。而如果我们偶然猜对（这对于我来说是偶然，而对于Kent Beck和Martin Fowler这样的大师来说则是辛勤工作的成果），那么恭喜您，TDD有可能成为您显著提升工作效率的一件法宝。请不要将信将疑，若即若离，因为任何一项新的技术——只要是从根本上改变人的行为方式的技术——就必然使得相信它的人越来越相信，不信的人越来越不信。这就好比学游泳，唯一能学会游泳的途径就是亲自下去游，除此之外别无他法。这也好比成功学，即使把卡耐基或希尔博士的书倒背如流也不能拥有积极的心态，可当你以积极的心态去成就了一番事业之后，你就再也离不开它了。相信我，TDD也是这样！想试用TDD的人们，请遵循下面的步骤：</p>
		<blockquote>
				<font style="FONT-WEIGHT: bold; FONT-SIZE: large; BACKGROUND-COLOR: rgb(220,220,220)">
						<table cellspacing="1" cellpadding="1" bgcolor="#dcdcdc" border="0">
								<tbody>
										<tr>
												<td align="middle">编写TestCase</td>
												<td>--&gt;</td>
												<td align="middle">实现TestCase</td>
												<td>--&gt;</td>
												<td align="middle">重构</td>
										</tr>
										<tr>
												<td align="middle">（确定范围和目标）</td>
												<td> </td>
												<td align="middle">（增加功能）</td>
												<td> </td>
												<td align="middle">（提升设计）</td>
										</tr>
								</tbody>
						</table>
				</font>
		</blockquote>
		<p>[友情提示：敏捷建模中的一个相当重要的实践被称为：Prove it With Code，这种想法和TDD不谋而合。]</p>
		<p>
				<font color="#ff0000" size="5">{TDD的优点}</font>
		</p>
		<ol>
				<p>
						<b>『充满吸引力的优点』</b>
				</p>
				<li>完工时完工。表明我可以很清楚的看到自己的这段工作已经结束了，而传统的方式很难知道什么时候编码工作结束了。 
</li>
				<li>全面正确的认识代码和利用代码，而传统的方式没有这个机会。 
</li>
				<li>为利用你成果的人提供Sample，无论它是要利用你的源代码，还是直接重用你提供的组件。 
</li>
				<li>开发小组间降低了交流成本，提高了相互信赖程度。 
</li>
				<li>避免了过渡设计。 
</li>
				<li>系统可以与详尽的测试集一起发布，从而对程序的将来版本的修改和扩展提供方便。 
</li>
				<li>TDD给了我们自信，让我们今天的问题今天解决，明天的问题明天解决，今天不能解决明天的问题，因为明天的问题还没有出现(没有TestCase)，除非有TestCase否则我决不写任何代码；明天也不必担心今天的问题，只要我亮了绿灯。 
<p></p><p><b>『不显而易见的优点』</b></p></li>
				<li>逃避了设计角色。对于一个敏捷的开发小组，每个人都在做设计。 
</li>
				<li>大部分时间代码处在高质量状态，100％的时间里成果是可见的。 
</li>
				<li>由于可以保证编写测试和编写代码的是相同的程序员，降低了理解代码所花费的成本。 
</li>
				<li>为减少文档和代码之间存在的细微的差别和由这种差别所引入的Bug作出杰出贡献。 
</li>
				<li>在预先设计和紧急设计之间建立一种平衡点，为你区分哪些设计该事先做、哪些设计该迭代时做提供了一个可靠的判断依据。 
<p><b>『有争议的优点』</b></p></li>
				<li>事实上提高了开发效率。每一个正在使用TDD并相信TDD的人都会相信这一点，但观望者则不同，不相信TDD的人甚至坚决反对这一点，这很正常，世界总是这样。 
</li>
				<li>发现比传统测试方式更多的Bug。 
</li>
				<li>使IDE的调试功能失去意义，或者应该说，避免了令人头痛的调试和节约了调试的时间。 
</li>
				<li>总是处在要么编程要么重构的状态下，不会使人抓狂。（两顶帽子） 
</li>
				<li>单元测试非常有趣。 </li>
		</ol>
		<p>
		</p>
		<font color="#ff0000" size="5">{TDD的步骤}</font>
		<blockquote>
				<font style="FONT-WEIGHT: bold; FONT-SIZE: large; BACKGROUND-COLOR: rgb(220,220,220)">
						<table cellspacing="1" cellpadding="1" bgcolor="#dcdcdc" border="0">
								<tbody>
										<tr>
												<td align="middle">编写TestCase</td>
												<td>--&gt;</td>
												<td align="middle">实现TestCase</td>
												<td>--&gt;</td>
												<td align="middle">重构</td>
										</tr>
										<tr>
												<td align="middle">（不可运行）</td>
												<td> </td>
												<td align="middle">（可运行）</td>
												<td> </td>
												<td align="middle">（重构）</td>
										</tr>
								</tbody>
						</table>
				</font>
		</blockquote>
		<table cellspacing="2" cellpadding="2" border="0">
				<tbody>
						<tr>
								<td>步骤</td>
								<td>制品</td>
						</tr>
						<tr>
								<td>（1）快速新增一个测试用例</td>
								<td>新的TestCase</td>
						</tr>
						<tr>
								<td>（2）编译所有代码，刚刚写的那个测试很可能编译不通过 </td>
								<td>原始的TODO List</td>
						</tr>
						<tr>
								<td>（3）做尽可能少的改动，让编译通过</td>
								<td>Interface</td>
						</tr>
						<tr>
								<td>（4）运行所有的测试，发现最新的测试不能编译通过</td>
								<td>－(Red Bar)</td>
						</tr>
						<tr>
								<td>（5）做尽可能少的改动，让测试通过</td>
								<td>Implementation</td>
						</tr>
						<tr>
								<td>（6）运行所有的测试，保证每个都能通过</td>
								<td>－(Green Bar)</td>
						</tr>
						<tr>
								<td>（7）重构代码，以消除重复设计</td>
								<td>Clean Code That Works</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font color="#ff0000" size="5">{FAQ}</font>
		</p>
		<p>
				<b>[什么时候重构？]</b>
				<br />如果您在软件公司工作，就意味着您成天都会和想通过重构改善代码质量的想法打交道，不仅您如此，您的大部分同事也都如此。可是，究竟什么时候该重构，什么情况下应该重构呢？我相信您和您的同事可能有很多不同的看法，最常见的答案是“该重构时重构”，“写不下去的时候重构”，和“下一次迭代开始之前重构”，或者干脆就是“最近没时间，就不重构了，下次有时间的时候重构吧”。正如您已经预见到我想说的——这些想法都是对重构的误解。重构不是一种构建软件的工具，不是一种设计软件的模式，也不是一个软件开发过程中的环节，正确理解重构的人应该把重构看成一种书写代码的方式，或习惯，重构时时刻刻有可能发生。在TDD中，除去编写测试用例和实现测试用例之外的所有工作都是重构，所以，没有重构任何设计都不能实现。至于什么时候重构嘛，还要分开看，有三句话是我的经验：实现测试用例时重构代码，完成某个特性时重构设计，产品的重构完成后还要记得重构一下测试用例哦。</p>
		<p>
				<b>[什么时候设计？]</b>
				<br />这个问题比前面一个要难回答的多，实话实说，本人在依照TDD开发软件的时候也常常被这个问题困扰，总是觉得有些问题应该在写测试用例之前定下来，而有些问题应该在新增一个一个测试用例的过程中自然出现，水到渠成。所以，我的建议是，设计的时机应该由开发者自己把握，不要受到TDD方式的限制，但是，不需要事先确定的事一定不能事先确定，免得捆住了自己的手脚。</p>
		<p>
				<b>[什么时候增加新的TestCase？]</b>
				<br />没事做的时候。通常我们认为，如果你要增加一个新的功能，那么先写一个不能通过的 TestCase；如果你发现了一个bug，那么先写一个不能通过的TestCase；如果你现在什么都没有，从0开始，请先写一个不能通过的 TestCase。所有的工作都是从一个TestCase开始。此外，还要注意的是，一些大师要求我们每次只允许有一个TestCase亮红灯，在这个 TestCase没有Green之前不可以写别的TestCase，这种要求可以适当考虑，但即使有多个TestCase亮红灯也不要紧，并未违反TDD 的主要精神。</p>
		<p>
				<b>[TestCase该怎么写？]</b>
				<br />测试用例的编写实际上就是两个过程：使用尚不存在的代码和定义这些代码的执行结果。所以一个 TestCase也就应该包括两个部分——场景和断言。第一次写TestCase的人会有很大的不适应的感觉，因为你之前所写的所有东西都是在解决问题，现在要你提出问题确实不大习惯，不过不用担心，你正在做正确的事情，而这个世界上最难的事情也不在于如何解决问题，而在于ask the right question！</p>
		<p>
				<b>[TDD能帮助我消除Bug吗？]</b>
				<br />答：不能！千万不要把“测试”和“除虫”混为一谈！“除虫”是指程序员通过自己的努力来减少bug的数量（消除bug这样的字眼我们还是不要讲为好^_^），而“测试”是指程序员书写产品以外的一段代码来确保产品能有效工作。虽然TDD所编写的测试用例在一定程度上为寻找bug提供了依据，但事实上，按照TDD的方式进行的软件开发是不可能通过TDD再找到bug的（想想我们前面说的“完工时完工”），你想啊，当我们的代码完成的时候，所有的测试用例都亮了绿灯，这时隐藏在代码中的bug一个都不会露出马脚来。</p>
		<p>但是，如果要问“测试”和“除虫”之间有什么联系，我相信还是有很多话可以讲的，比如TDD事实上减少了bug的数量，把查找bug战役的关注点从全线战场提升到代码战场以上。还有，bug的最可怕之处不在于隐藏之深，而在于满天遍野。如果你发现了一个用户很不容易才能发现的bug，那么不一定对工作做出了什么杰出贡献，但是如果你发现一段代码中，bug的密度或离散程度过高，那么恭喜你，你应该抛弃并重写这段代码了。TDD避免了这种情况，所以将寻找bug的工作降低到了一个新的低度。</p>
		<p>
				<b>[我该为一个Feature编写TestCase还是为一个类编写TestCase？]</b>
				<br />初学者常问的问题。虽然我们从TDD 的说明书上看到应该为一个特性编写相应的TestCase，但为什么著名的TDD大师所写的TestCase都是和类/方法一一对应的呢？为了解释这个问题，我和我的同事们都做了很多试验，最后我们得到了一个结论，虽然我不知道是否正确，但是如果您没有答案，可以姑且相信我们。</p>
		<p>我们的研究结果表明，通常在一个特性的开发开始时，我们针对特性编写测试用例，如果您发现这个特性无法用TestCase表达，那么请将这个特性细分，直至您可以为手上的特性写出TestCase为止。从这里开始是最安全的，它不会导致任何设计上重大的失误。但是，随着您不断的重构代码，不断的重构 TestCase，不断的依据TDD的思想做下去，最后当产品伴随测试用例集一起发布的时候，您就会不经意的发现经过重构以后的测试用例很可能是和产品中的类/方法一一对应的。</p>
		<p>
				<b>[什么时候应该将全部测试都运行一遍？]</b>
				<br />Good Question！大师们要求我们每次重构之后都要完整的运行一遍测试用例。这个要求可以理解，因为重构很可能会改变整个代码的结构或设计，从而导致不可预见的后果，但是如果我正在开发的是一个ERP怎么办？运行一遍完整的测试用例可能将花费数个小时，况且现在很多重构都是由工具做到的，这个要求的可行性和前提条件都有所动摇。所以我认为原则上你可以挑几个你觉得可能受到本次重构影响的TestCase去run，但是如果运行整个测试包只要花费数秒的时间，那么不介意你按大师的要求去做。</p>
		<p>
				<b>[什么时候改进一个TestCase？]</b>
				<br />增加的测试用例或重构以后的代码导致了原来的TestCase的失去了效果，变得无意义，甚至可能导致错误的结果，这时是改进TestCase的最好时机。但是有时你会发现，这样做仅仅导致了原来的TestCase在设计上是臃肿的，或者是冗余的，这都不要紧，只要它没有失效，你仍然不用去改进它。记住，TestCase不是你的产品，它不要好看，也不要怎么太科学，甚至没有性能要求，它只要能完成它的使命就可以了——这也证明了我们后面所说的“用Ctrl-C/Ctrl-V编写测试用例”的可行性。</p>
		<p>但是，美国人的想法其实跟我们还是不太一样，拿托尼巴赞的MindMap来说吧，其实画MindMap只是为了表现自己的思路，或记忆某些重要的事情，但托尼却建议大家把MindMap画成一件艺术品，甚至还有很多艺术家把自己画的抽象派MindMap拿出来帮助托尼做宣传。同样，大师们也要求我们把TestCase写的跟代码一样质量精良，可我想说的是，现在国内有几个公司能把产品的代码写的精良？？还是一步一步慢慢来吧。</p>
		<p>
				<b>[为什么原来通过的测试用例现在不能通过了？]</b>
				<br />这是一个警报，Red Alert！它可能表达了两层意思——都不是什么好意思——1）你刚刚进行的重构可能失败了，或存在一些错误未被发现，至少重构的结果和原来的代码不等价了。2）你刚刚增加的TestCase所表达的意思跟前面已经有的TestCase相冲突，也就是说，新增的功能违背了已有的设计，这种情况大部分可能是之前的设计错了。但无论哪错了，无论是那层意思，想找到这个问题的根源都比TDD的正常工作要难。</p>
		<p>
				<b>[我怎么知道那里该有一个方法还是该有一个类？]</b>
				<br />这个问题也是常常出现在我的脑海中，无论你是第一次接触TDD或者已经成为 TDD专家，这个问题都会缠绕着你不放。不过问题的答案可以参考前面的“什么时候设计”一节，答案不是唯一的。其实多数时候你不必考虑未来，今天只做今天的事，只要有重构工具，从方法到类和从类到方法都很容易。</p>
		<p>
				<b>[我要写一个TestCase，可是不知道从哪里开始？]</b>
				<br />从最重要的事开始，what matters most？从脚下开始，从手头上的工作开始，从眼前的事开始。从一个没有UI的核心特性开始，从算法开始，或者从最有可能耽误时间的模块开始，从一个最严重的bug开始。这是TDD主义者和鼠目寸光者的一个共同点，不同点是前者早已成竹在胸。</p>
		<p>
				<b>[为什么我的测试总是看起来有点愚蠢？]</b>
				<br />哦？是吗？来，握个手，我的也是！不必担心这一点，事实上，大师们给的例子也相当愚蠢，比如一个极端的例子是要写一个两个int变量相加的方法，大师先断言2+3=5，再断言5+5=10，难道这些代码不是很愚蠢吗？其实这只是一个极端的例子，当你初次接触TDD时，写这样的代码没什么不好，以后当你熟练时就会发现这样写没必要了，要记住，谦虚是通往TDD的必经之路！从经典开发方法转向TDD就像从面向过程转向面向对象一样困难，你可能什么都懂，但你写出来的类没有一个纯OO的！我的同事还告诉我真正的太极拳，其速度是很快的，不比任何一个快拳要慢，但是初学者（通常是指学习太极拳的前10年）太不容易把每个姿势都做对，所以只能慢慢来。</p>
		<p>
				<b>[什么场合不适用TDD？]</b>
				<br />问的好，确实有很多场合不适合使用TDD。比如对软件质量要求极高的军事或科研产品——神州六号，人命关天的软件——医疗设备，等等，再比如设计很重要必须提前做好的软件，这些都不适合TDD，但是不适合TDD不代表不能写TestCase，只是作用不同，地位不同罢了。</p>
		<p>
				<font color="#ff0000" size="5">{Best Practise}</font>
		</p>
		<p>
				<b>[微笑面对编译错误]</b>
				<br />学生时代最害怕的就是编译错误，编译错误可能会被老师视为上课不认真听课的证据，或者同学间相互嘲笑的砝码。甚至离开学校很多年的老程序员依然害怕它就像害怕迟到一样，潜意识里似乎编译错误极有可能和工资挂钩（或者和智商挂钩，反正都不是什么好事）。其实，只要提交到版本管理的代码没有编译错误就可以了，不要担心自己手上的代码的编译错误，通常，编译错误都集中在下面三个方面：<br />（1）你的代码存在低级错误<br />（2）由于某些Interface的实现尚不存在，所以被测试代码无法编译<br />（3）由于某些代码尚不存在，所以测试代码无法编译<br />请注意第二点与第三点完全不同，前者表明设计已存在，而实现不存在导致的编译错误；后者则指仅有TestCase而其它什么都没有的情况，设计和实现都不存在，没有Interface也没有Implementation。</p>
		<p>另外，编译器还有一个优点，那就是以最敏捷的身手告诉你，你的代码中有那些错误。当然如果你拥有Eclipse这样可以及时提示编译错误的IDE，就不需要这样的功能了。</p>
		<p>
				<b>[重视你的计划清单]</b>
				<br />在非TDD的情况下，尤其是传统的瀑布模型的情况下，程序员不会不知道该做什么，事实上，总是有设计或者别的什么制品在引导程序员开发。但是在TDD的情况下，这种优势没有了，所以一个计划清单对你来说十分重要，因为你必须自己发现该做什么。不同性格的人对于这一点会有不同的反应，我相信平时做事没什么计划要依靠别人安排的人（所谓将才）可能略有不适应，不过不要紧，Tasks和Calendar（又称效率手册）早已成为现代上班族的必备工具了；而平时工作生活就很有计划性的人，比如我:)，就会更喜欢这种自己可以掌控Plan的方式了。</p>
		<p>
				<b>[废黜每日代码质量检查]</b>
				<br />如果我没有记错的话，PSP对于个人代码检查的要求是蛮严格的，而同样是在针对个人的问题上， TDD却建议你废黜每日代码质量检查，别起疑心，因为你总是在做TestCase要求你做的事情，并且总是有办法（自动的）检查代码有没有做到这些事情 ——红灯停绿灯行，所以每日代码检查的时间可能被节省，对于一个严格的PSP实践者来说，这个成本还是很可观的！</p>
		<p>此外，对于每日代码质量检查的另一个好处，就是帮助你认识自己的代码，全面的从宏观、微观、各个角度审视自己的成果，现在，当你依照TDD做事时，这个优点也不需要了，还记得前面说的TDD的第二个优点吗，因为你已经全面的使用了一遍你的代码，这完全可以达到目的。</p>
		<p>但是，问题往往也并不那么简单，现在有没有人能告诉我，我如何全面审视我所写的测试用例呢？别忘了，它们也是以代码的形式存在的哦。呵呵，但愿这个问题没有把你吓到，因为我相信到目前为止，它还不是瓶颈问题，况且在编写产品代码的时候你还是会自主的发现很多测试代码上的没考虑到的地方，可以就此修改一下。道理就是如此，世界上没有任何方法能代替你思考的过程，所以也没有任何方法能阻止你犯错误，TDD仅能让你更容易发现这些错误而已。</p>
		<p>
				<b>[如果无法完成一个大的测试，就从最小的开始]</b>
				<br />如果我无法开始怎么办，教科书上有个很好的例子：我要写一个电影列表的类，我不知道如何下手，如何写测试用例，不要紧，首先想象静态的结果，如果我的电影列表刚刚建立呢，那么它应该是空的，OK，就写这个断言吧，断言一个刚刚初始化的电影列表是空的。这不是愚蠢，这是细节，奥运会五项全能的金牌得主玛丽莲·金是这样说的：“成功人士的共同点在于……如果目标不够清晰，他们会首先做通往成功道路上的每一个细小步骤……”。</p>
		<p>
				<b>[尝试编写自己的xUnit]</b>
				<br />Kent Beck建议大家每当接触一个新的语言或开发平台的时候，就自己写这个语言或平台的xUnit，其实几乎所有常用的语言和平台都已经有了自己的 xUnit，而且都是大同小异，但是为什么大师给出了这样的建议呢。其实Kent Beck的意思是说通过这样的方式你可以很快的了解这个语言或平台的特性，而且xUnit确实很简单，只要知道原理很快就能写出来。这对于那些喜欢自己写底层代码的人，或者喜欢控制力的人而言是个好消息。</p>
		<p>
				<b>[善于使用Ctrl-C/Ctrl-V来编写TestCase]</b>
				<br />不必担心TestCase会有代码冗余的问题，让它冗余好了。</p>
		<p>
				<b>[永远都是功能First，改进可以稍后进行]</b>
				<br />上面这个标题还可以改成另外一句话：避免过渡设计！</p>
		<p>
				<b>[淘汰陈旧的用例]</b>
				<br />舍不得孩子套不着狼。不要可惜陈旧的用例，因为它们可能从概念上已经是错误的了，或仅仅会得出错误的结果，或者在某次重构之后失去了意义。当然也不一定非要删除它们，从TestSuite中除去（JUnit）或加上Ignored（NUnit）标签也是一个好办法。</p>
		<p>
				<b>[用TestCase做试验]</b>
				<br />如果你在开始某个特性或产品的开发之前对某个领域不太熟悉或一无所知，或者对自己在该领域里的能力一无所知，那么你一定会选择做试验，在有单元测试作工具的情况下，建议你用TestCase做试验，这看起来就像你在写一个验证功能是否实现的 TestCase一样，而事实上也一样，只不过你所验证的不是代码本身，而是这些代码所依赖的环境。</p>
		<p>
				<b>[TestCase之间应该尽量独立]</b>
				<br />保证单独运行一个TestCase是有意义的。</p>
		<p>
				<b>[不仅测试必须要通过的代码，还要测试必须不能通过的代码]</b>
				<br />这是一个小技巧，也是不同于设计思路的东西。像越界的值或者乱码，或者类型不符的变量，这些输入都可能会导致某个异常的抛出，或者导致一个标示“illegal parameters”的返回值，这两种情况你都应该测试。当然我们无法枚举所有错误的输入或外部环境，这就像我们无法枚举所有正确的输入和外部环境一样，只要TestCase能说明问题就可以了。</p>
		<p>
				<b>[编写代码的第一步，是在TestCase中用Ctrl-C]</b>
				<br />这是一个高级技巧，呃，是的，我是这个意思，我不是说这个技巧难以掌握，而是说这个技巧当且仅当你已经是一个TDD高手时，你才能体会到它的魅力。多次使用TDD的人都有这样的体会，既然我的TestCase已经写的很好了，很能说明问题，为什么我的代码不能从TestCase拷贝一些东西来呢。当然，这要求你的TestCase已经具有很好的表达能力，比如断言f (5)=125的方式显然没有断言f(5)=5^(5-2)表达更多的内容。</p>
		<p>
				<b>[测试用例包应该尽量设计成可以自动运行的]</b>
				<br />如果产品是需要交付源代码的，那我们应该允许用户对代码进行修改或扩充后在自己的环境下run整个测试用例包。既然通常情况下的产品是可以自动运行的，那为什么同样作为交付用户的制品，测试用例包就不是自动运行的呢？即使产品不需要交付源代码，测试用例包也应该设计成可以自动运行的，这为测试部门或下一版本的开发人员提供了极大的便利。</p>
		<p>
				<b>[只亮一盏红灯]</b>
				<br />大师的建议，前面已经提到了，仅仅是建议。</p>
		<p>
				<b>[用TestCase描述你发现的bug]</b>
				<br />如果你在另一个部门的同事使用了你的代码，并且，他发现了一个bug，你猜他会怎么做？他会立即走到你的工位边上，大声斥责说：“你有bug！”吗？如果他胆敢这样对你，对不起，你一定要冷静下来，不要当面回骂他，相反你可以微微一笑，然后心平气和的对他说：“哦，是吗？那么好吧，给我一个TestCase证明一下。”现在局势已经倒向你这一边了，如果他还没有准备好回答你这致命的一击，我猜他会感到非常羞愧，并在内心责怪自己太莽撞。事实上，如果他的TestCase没有过多的要求你的代码（而是按你们事前的契约），并且亮了红灯，那么就可以确定是你的bug，反之，对方则无理了。用TestCase描述bug的另一个好处是，不会因为以后的修改而再次暴露这个bug，它已经成为你发布每一个版本之前所必须检查的内容了。</p>
		<p>
				<font color="#ff0000" size="5">{关于单元测试}</font>
		</p>
		<p>单元测试的目标是</p>
		<blockquote>
				<p>
						<font style="FONT-WEIGHT: bold; FONT-SIZE: large; BACKGROUND-COLOR: rgb(220,220,220)">Keep the bar green to keep the code clean</font>
				</p>
		</blockquote>
		<p>这句话的含义是，事实上我们只做两件事情：让代码奏效（Keep the bar green）和让代码洁净（Keep the code clean），前者是把事情做对，后者是把事情做好，两者既是TDD中的两顶帽子，又是xUnit架构中的因果关系。</p>
		<p>单元测试作为软件测试的一个类别，并非是xUnit架构创造的，而是很早就有了。但是xUnit架构使得单元测试变得直接、简单、高效和规范，这也是单元测试最近几年飞速发展成为衡量一个开发工具和环境的主要指标之一的原因。正如Martin Fowler所说：“软件工程有史以来从没有如此众多的人大大收益于如此简单的代码！”而且多数语言和平台的xUnit架构都是大同小异，有的仅是语言不同，其中最有代表性的是JUnit和NUnit，后者是前者的创新和扩展。一个单元测试框架xUnit应该：1）使每个TestCase独立运行；2）使每个TestCase可以独立检测和报告错误；3）易于在每次运行之前选择TestCase。下面是我枚举出的xUnit框架的概念，这些概念构成了当前业界单元测试理论和工具的核心：</p>
		<p>
				<b>[测试方法/TestMethod]</b>
				<br />测试的最小单位，直接表示为代码。</p>
		<p>
				<b>[测试用例/TestCase]</b>
				<br />由多个测试方法组成，是一个完整的对象，是很多TestRunner执行的最小单位。</p>
		<p>
				<b>[测试容器/TestSuite]</b>
				<br />由多个测试用例构成，意在把相同含义的测试用例手动安排在一起，TestSuite可以呈树状结构因而便于管理。在实现时，TestSuite形式上往往也是一个TestCase或TestFixture。</p>
		<p>
				<b>[断言/Assertion]</b>
				<br />断言一般有三类，分别是比较断言（如assertEquals），条件断言（如isTrue），和断言工具（如fail）。</p>
		<p>
				<b>[测试设备/TestFixture]</b>
				<br />为每个测试用例安排一个SetUp方法和一个TearDown方法，前者用于在执行该测试用例或该用例中的每个测试方法前调用以初始化某些内容，后者在执行该测试用例或该用例中的每个方法之后调用，通常用来消除测试对系统所做的修改。</p>
		<p>
				<b>[期望异常/Expected Exception]</b>
				<br />期望该测试方法抛出某种指定的异常，作为一个“断言”内容，同时也防止因为合情合理的异常而意外的终止了测试过程。</p>
		<p>
				<b>[种类/Category]</b>
				<br />为测试用例分类，实际使用时一般有TestSuite就不再使用Category，有Category就不再使用TestSuite。</p>
		<p>
				<b>[忽略/Ignored]</b>
				<br />设定该测试用例或测试方法被忽略，也就是不执行的意思。有些被抛弃的TestCase不愿删除，可以定为Ignored。</p>
		<p>
				<b>[测试执行器/TestRunner]</b>
				<br />执行测试的工具，表示以何种方式执行测试，别误会，这可不是在代码中规定的，完全是与测试内容无关的行为。比如文本方式，AWT方式，swing方式，或者Eclipse的一个视图等等。</p>
		<p>
				<font color="#ff0000" size="5">{实例：Fibonacci数列}</font>
		</p>
		<p>下面的Sample展示TDDer是如何编写一个旨在产生Fibonacci数列的方法。<br />（1）首先写一个TC，断言fib(1) = 1;fib(2) = 1;这表示该数列的第一个元素和第二个元素都是1。</p>
		<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
				<div>
						<span style="COLOR: rgb(0,0,255)">public</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">void</span>
						<span style="COLOR: rgb(0,0,0)"> testFab() {<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">));<br />}</span>
				</div>
		</div>
		<p>（2）上面这段代码不能编译通过，Great！——是的，我是说Great！当然，如果你正在用的是Eclipse那你不需要编译，Eclipse 会告诉你不存在fib方法，单击mark会问你要不要新建一个fib方法，Oh，当然！为了让上面那个TC能通过，我们这样写：</p>
		<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
				<div>
						<span style="COLOR: rgb(0,0,255)">public</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> fib( </span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> n ) {<br />        </span>
						<span style="COLOR: rgb(0,0,255)">return</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">;<br />}</span>
				</div>
		</div>
		<p>（3）现在那个TC亮了绿灯，wow！应该庆祝一下了。接下来要增加TC的难度了，测第三个元素。</p>
		<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
				<div>
						<span style="COLOR: rgb(0,0,255)">public</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">void</span>
						<span style="COLOR: rgb(0,0,0)"> testFab() {<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">3</span>
						<span style="COLOR: rgb(0,0,0)">));<br />}</span>
				</div>
		</div>
		<p>不过这样写还不太好看，不如这样写：</p>
		<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
				<div>
						<span style="COLOR: rgb(0,0,255)">public</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">void</span>
						<span style="COLOR: rgb(0,0,0)"> testFab() {<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(fib(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">)</span>
						<span style="COLOR: rgb(0,0,0)">+</span>
						<span style="COLOR: rgb(0,0,0)">fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">), fib(</span>
						<span style="COLOR: rgb(0,0,0)">3</span>
						<span style="COLOR: rgb(0,0,0)">));<br />}</span>
				</div>
		</div>
		<p>（4）新增加的断言导致了红灯，为了扭转这一局势我们这样修改fib方法，其中部分代码是从上面的代码中Ctrl-C/Ctrl-V来的：</p>
		<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
				<div>
						<span style="COLOR: rgb(0,0,255)">public</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> fib( </span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> n ) {<br />        </span>
						<span style="COLOR: rgb(0,0,255)">if</span>
						<span style="COLOR: rgb(0,0,0)"> ( n </span>
						<span style="COLOR: rgb(0,0,0)">==</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">3</span>
						<span style="COLOR: rgb(0,0,0)"> ) </span>
						<span style="COLOR: rgb(0,0,255)">return</span>
						<span style="COLOR: rgb(0,0,0)"> fib(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">)</span>
						<span style="COLOR: rgb(0,0,0)">+</span>
						<span style="COLOR: rgb(0,0,0)">fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">);<br />        </span>
						<span style="COLOR: rgb(0,0,255)">return</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">;<br />}</span>
				</div>
		</div>
		<p>（5）天哪，这真是个贱人写的代码！是啊，不是吗？因为TC就是产品的蓝本，产品只要恰好满足TC就ok。所以事情发展到这个地步不是fib方法的错，而是TC的错，于是TC还要进一步要求：</p>
		<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
				<div>
						<span style="COLOR: rgb(0,0,255)">public</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">void</span>
						<span style="COLOR: rgb(0,0,0)"> testFab() {<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(fib(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">)</span>
						<span style="COLOR: rgb(0,0,0)">+</span>
						<span style="COLOR: rgb(0,0,0)">fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">), fib(</span>
						<span style="COLOR: rgb(0,0,0)">3</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">)</span>
						<span style="COLOR: rgb(0,0,0)">+</span>
						<span style="COLOR: rgb(0,0,0)">fib(</span>
						<span style="COLOR: rgb(0,0,0)">3</span>
						<span style="COLOR: rgb(0,0,0)">), fib(</span>
						<span style="COLOR: rgb(0,0,0)">4</span>
						<span style="COLOR: rgb(0,0,0)">));<br />}</span>
				</div>
		</div>
		<p>（6）上有政策下有对策。</p>
		<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
				<div>
						<span style="COLOR: rgb(0,0,255)">public</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> fib( </span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> n ) {<br />        </span>
						<span style="COLOR: rgb(0,0,255)">if</span>
						<span style="COLOR: rgb(0,0,0)"> ( n </span>
						<span style="COLOR: rgb(0,0,0)">==</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">3</span>
						<span style="COLOR: rgb(0,0,0)"> ) </span>
						<span style="COLOR: rgb(0,0,255)">return</span>
						<span style="COLOR: rgb(0,0,0)"> fib(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">)</span>
						<span style="COLOR: rgb(0,0,0)">+</span>
						<span style="COLOR: rgb(0,0,0)">fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">);<br />        </span>
						<span style="COLOR: rgb(0,0,255)">if</span>
						<span style="COLOR: rgb(0,0,0)"> ( n </span>
						<span style="COLOR: rgb(0,0,0)">==</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">4</span>
						<span style="COLOR: rgb(0,0,0)"> ) </span>
						<span style="COLOR: rgb(0,0,255)">return</span>
						<span style="COLOR: rgb(0,0,0)"> fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">)</span>
						<span style="COLOR: rgb(0,0,0)">+</span>
						<span style="COLOR: rgb(0,0,0)">fib(</span>
						<span style="COLOR: rgb(0,0,0)">3</span>
						<span style="COLOR: rgb(0,0,0)">);<br />        </span>
						<span style="COLOR: rgb(0,0,255)">return</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">;<br />}</span>
				</div>
		</div>
		<p>（7）好了，不玩了。现在已经不是贱不贱的问题了，现在的问题是代码出现了冗余，所以我们要做的是——重构：</p>
		<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
				<div>
						<span style="COLOR: rgb(0,0,255)">public</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> fib( </span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> n ) {<br />        </span>
						<span style="COLOR: rgb(0,0,255)">if</span>
						<span style="COLOR: rgb(0,0,0)"> ( n </span>
						<span style="COLOR: rgb(0,0,0)">==</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">||</span>
						<span style="COLOR: rgb(0,0,0)"> n </span>
						<span style="COLOR: rgb(0,0,0)">==</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)"> ） </span>
						<span style="COLOR: rgb(0,0,255)">return</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">;<br />        </span>
						<span style="COLOR: rgb(0,0,255)">else</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">return</span>
						<span style="COLOR: rgb(0,0,0)"> fib( n </span>
						<span style="COLOR: rgb(0,0,0)">-</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)"> ) </span>
						<span style="COLOR: rgb(0,0,0)">+</span>
						<span style="COLOR: rgb(0,0,0)"> fib( n </span>
						<span style="COLOR: rgb(0,0,0)">-</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)"> );<br />}</span>
				</div>
		</div>
		<p>（8）好，现在你已经fib方法已经写完了吗？错了，一个危险的错误，你忘了错误的输入了。我们令0表示Fibonacci中没有这一项。</p>
		<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
				<div>
						<span style="COLOR: rgb(0,0,255)">public</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">void</span>
						<span style="COLOR: rgb(0,0,0)"> testFab() {<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(fib(</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">)</span>
						<span style="COLOR: rgb(0,0,0)">+</span>
						<span style="COLOR: rgb(0,0,0)">fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">), fib(</span>
						<span style="COLOR: rgb(0,0,0)">3</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(fib(</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">)</span>
						<span style="COLOR: rgb(0,0,0)">+</span>
						<span style="COLOR: rgb(0,0,0)">fib(</span>
						<span style="COLOR: rgb(0,0,0)">3</span>
						<span style="COLOR: rgb(0,0,0)">), fib(</span>
						<span style="COLOR: rgb(0,0,0)">4</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">0</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">0</span>
						<span style="COLOR: rgb(0,0,0)">));<br />        assertEquals(</span>
						<span style="COLOR: rgb(0,0,0)">0</span>
						<span style="COLOR: rgb(0,0,0)">, fib(</span>
						<span style="COLOR: rgb(0,0,0)">-</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">));<br />}</span>
				</div>
		</div>
		<p>then change the method fib to make the bar grean：</p>
		<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
				<div>
						<span style="COLOR: rgb(0,0,255)">public</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> fib( </span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> n ) {<br />        </span>
						<span style="COLOR: rgb(0,0,255)">if</span>
						<span style="COLOR: rgb(0,0,0)"> ( n </span>
						<span style="COLOR: rgb(0,0,0)">&lt;=</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">0</span>
						<span style="COLOR: rgb(0,0,0)"> ) </span>
						<span style="COLOR: rgb(0,0,255)">return</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">0</span>
						<span style="COLOR: rgb(0,0,0)">;<br />        </span>
						<span style="COLOR: rgb(0,0,255)">if</span>
						<span style="COLOR: rgb(0,0,0)"> ( n </span>
						<span style="COLOR: rgb(0,0,0)">==</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">||</span>
						<span style="COLOR: rgb(0,0,0)"> n </span>
						<span style="COLOR: rgb(0,0,0)">==</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)"> ） </span>
						<span style="COLOR: rgb(0,0,255)">return</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">;<br />        </span>
						<span style="COLOR: rgb(0,0,255)">else</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">return</span>
						<span style="COLOR: rgb(0,0,0)"> fib( n </span>
						<span style="COLOR: rgb(0,0,0)">-</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)"> ) </span>
						<span style="COLOR: rgb(0,0,0)">+</span>
						<span style="COLOR: rgb(0,0,0)"> fib( n </span>
						<span style="COLOR: rgb(0,0,0)">-</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)"> );<br />}</span>
				</div>
		</div>
		<p>（9）下班前最后一件事情，把TC也重构一下：</p>
		<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(230,230,230) 0% 50%; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid; moz-background-clip: initial; moz-background-origin: initial; moz-background-inline-policy: initial">
				<div>
						<span style="COLOR: rgb(0,0,255)">public</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,255)">void</span>
						<span style="COLOR: rgb(0,0,0)"> testFab() {<br />        </span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> cases[][] </span>
						<span style="COLOR: rgb(0,0,0)">=</span>
						<span style="COLOR: rgb(0,0,0)"> {<br />                {</span>
						<span style="COLOR: rgb(0,0,0)">0</span>
						<span style="COLOR: rgb(0,0,0)">, </span>
						<span style="COLOR: rgb(0,0,0)">0</span>
						<span style="COLOR: rgb(0,0,0)">}, {</span>
						<span style="COLOR: rgb(0,0,0)">-</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, </span>
						<span style="COLOR: rgb(0,0,0)">0</span>
						<span style="COLOR: rgb(0,0,0)">},  </span>
						<span style="COLOR: rgb(0,128,0)">//</span>
						<span style="COLOR: rgb(0,128,0)">the wrong parameters</span>
						<span style="COLOR: rgb(0,128,0)">
								<br />
						</span>
						<span style="COLOR: rgb(0,0,0)">                {</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">, </span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">}, {</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">, </span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">}};  </span>
						<span style="COLOR: rgb(0,128,0)">//</span>
						<span style="COLOR: rgb(0,128,0)">the first 2 elements</span>
						<span style="COLOR: rgb(0,128,0)">
								<br />
						</span>
						<span style="COLOR: rgb(0,0,0)">
								<br />        </span>
						<span style="COLOR: rgb(0,0,255)">for</span>
						<span style="COLOR: rgb(0,0,0)"> (</span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> i </span>
						<span style="COLOR: rgb(0,0,0)">=</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">0</span>
						<span style="COLOR: rgb(0,0,0)">; i </span>
						<span style="COLOR: rgb(0,0,0)">&lt;</span>
						<span style="COLOR: rgb(0,0,0)"> cases.length; i</span>
						<span style="COLOR: rgb(0,0,0)">++</span>
						<span style="COLOR: rgb(0,0,0)">)<br />                assertEquals( cases[i][</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">], fib(cases[i][</span>
						<span style="COLOR: rgb(0,0,0)">0</span>
						<span style="COLOR: rgb(0,0,0)">]) );<br /><br />        </span>
						<span style="COLOR: rgb(0,128,0)">//</span>
						<span style="COLOR: rgb(0,128,0)">the rest elements</span>
						<span style="COLOR: rgb(0,128,0)">
								<br />
						</span>
						<span style="COLOR: rgb(0,0,0)">        </span>
						<span style="COLOR: rgb(0,0,255)">for</span>
						<span style="COLOR: rgb(0,0,0)"> (</span>
						<span style="COLOR: rgb(0,0,255)">int</span>
						<span style="COLOR: rgb(0,0,0)"> i </span>
						<span style="COLOR: rgb(0,0,0)">=</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">3</span>
						<span style="COLOR: rgb(0,0,0)">; i </span>
						<span style="COLOR: rgb(0,0,0)">&lt;</span>
						<span style="COLOR: rgb(0,0,0)"> </span>
						<span style="COLOR: rgb(0,0,0)">20</span>
						<span style="COLOR: rgb(0,0,0)">; i</span>
						<span style="COLOR: rgb(0,0,0)">++</span>
						<span style="COLOR: rgb(0,0,0)">)<br />                assertEquals(fib(i</span>
						<span style="COLOR: rgb(0,0,0)">-</span>
						<span style="COLOR: rgb(0,0,0)">1</span>
						<span style="COLOR: rgb(0,0,0)">)</span>
						<span style="COLOR: rgb(0,0,0)">+</span>
						<span style="COLOR: rgb(0,0,0)">fib(i</span>
						<span style="COLOR: rgb(0,0,0)">-</span>
						<span style="COLOR: rgb(0,0,0)">2</span>
						<span style="COLOR: rgb(0,0,0)">), fib(i));<br />}</span>
				</div>
		</div>
		<p>（10）打完收工。</p>
		<p>
				<font color="#ff0000" size="5">{关于本文的写作}</font>
		</p>
		<p>在本文的写作过程中，作者也用到了TDD的思维，事实上作者先构思要写一篇什么样的文章，然后写出这篇文章应该满足的几个要求，包括功能的要求（要写些什么）和性能的要求（可读性如何）和质量的要求（文字的要求），这些要求起初是一个也达不到的（因为正文还一个字没有），在这种情况下作者的文章无法编译通过，为了达到这些要求，作者不停的写啊写啊，终于在花尽了两个月的心血之后完成了当初既定的所有要求（make the bar green），随后作者整理了一下文章的结构（重构），在满意的提交给了Blog系统之后，作者穿上了一件绿色的汗衫，趴在地上，学了两声青蛙叫。。。。。。。^_^</p>
		<p>
				<font color="#ff0000" size="5">{后记：Martin Fowler在中国}</font>
		</p>
		<p>从本文正式完成到发表的几个小时里，我偶然读到了Martin Fowler先生北京访谈录，其间提到了很多对测试驱动开发的看法，摘抄在此：</p>
		<blockquote>
				<p>
						<font color="#008000">Martin Fowler：当然（值得花一半的时间来写单元测试）！因为单元测试能够使你更快的完成工作。无数次的实践已经证明这一点。你的时间越是紧张，就越要写单元测试，它看上去慢，但实际上能够帮助你更快、更舒服地达到目的。<br />Martin Fowler：什么叫重要？什么叫不重要？这是需要逐渐认识的，不是想当然的。我为绝大多数的模块写单元测试，是有点烦人，但是当你意识到这工作的价值时，你会欣然的。<br />Martin Fowler：对全世界的程序员我都是那么几条建议：……第二，学习测试驱动开发，这种新的方法会改变你对于软件开发的看法。……</font>
				</p>
				<p align="right">——《程序员》，2005年7月刊</p>
		</blockquote>
		<p>
				<font color="#ff0000" size="5">{鸣谢}</font>
		</p>
		<p>
				<a href="/fhawk">fhawk</a>
				<br />Dennis Chen<br /><a href="http://xdingding.cnblogs.com/">般若菩提</a><br /><a href="http://c2.com/ppr/about/author/kent.html">Kent Beck</a><br /><a href="http://www.martinfowler.com/">Martin Fowler</a><br /><a href="http://c2.com/">c2.com</a></p>
		<p>（转载本文需注明出处：Brian Sun @ 爬树的泡泡[http://www.blogjava.net/briansun]）</p>
<img src ="http://www.blogjava.net/xzclog/aggbug/72761.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xzclog/" target="_blank">xzc</a> 2006-09-29 11:43 <a href="http://www.blogjava.net/xzclog/archive/2006/09/29/72761.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>