﻿<?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>C++博客-不倦的候鸟成长日记</title><link>http://www.cppblog.com/shuiyuan2004/</link><description>——候鸟，候补的菜鸟也</description><language>zh-cn</language><lastBuildDate>Tue, 14 Apr 2026 23:09:10 GMT</lastBuildDate><pubDate>Tue, 14 Apr 2026 23:09:10 GMT</pubDate><ttl>60</ttl><item><title>[转]测试驱动开发全功略</title><link>http://www.cppblog.com/shuiyuan2004/archive/2008/03/30/45751.html</link><dc:creator>不倦</dc:creator><author>不倦</author><pubDate>Sun, 30 Mar 2008 08:42:00 GMT</pubDate><guid>http://www.cppblog.com/shuiyuan2004/archive/2008/03/30/45751.html</guid><wfw:comment>http://www.cppblog.com/shuiyuan2004/comments/45751.html</wfw:comment><comments>http://www.cppblog.com/shuiyuan2004/archive/2008/03/30/45751.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/shuiyuan2004/comments/commentRss/45751.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shuiyuan2004/services/trackbacks/45751.html</trackback:ping><description><![CDATA[<p>From: Brian Sun @ 爬树的泡泡[http://www.blogjava.net/briansun]</p>
<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>&nbsp;</td>
            <td align=middle>（增加功能）</td>
            <td>&nbsp;</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><strong>『充满吸引力的优点』</strong> </p>
    <li>完工时完工。表明我可以很清楚的看到自己的这段工作已经结束了，而传统的方式很难知道什么时候编码工作结束了。
    <li>全面正确的认识代码和利用代码，而传统的方式没有这个机会。
    <li>为利用你成果的人提供Sample，无论它是要利用你的源代码，还是直接重用你提供的组件。
    <li>开发小组间降低了交流成本，提高了相互信赖程度。
    <li>避免了过渡设计。
    <li>系统可以与详尽的测试集一起发布，从而对程序的将来版本的修改和扩展提供方便。
    <li>TDD给了我们自信，让我们今天的问题今天解决，明天的问题明天解决，今天不能解决明天的问题，因为明天的问题还没有出现(没有TestCase)，除非有TestCase否则我决不写任何代码；明天也不必担心今天的问题，只要我亮了绿灯。
    <p><strong>『不显而易见的优点』</strong></p>
    <li>逃避了设计角色。对于一个敏捷的开发小组，每个人都在做设计。
    <li>大部分时间代码处在高质量状态，100％的时间里成果是可见的。
    <li>由于可以保证编写测试和编写代码的是相同的程序员，降低了理解代码所花费的成本。
    <li>为减少文档和代码之间存在的细微的差别和由这种差别所引入的Bug作出杰出贡献。
    <li>在预先设计和紧急设计之间建立一种平衡点，为你区分哪些设计该事先做、哪些设计该迭代时做提供了一个可靠的判断依据。
    <p><strong>『有争议的优点』</strong></p>
    <li>事实上提高了开发效率。每一个正在使用TDD并相信TDD的人都会相信这一点，但观望者则不同，不相信TDD的人甚至坚决反对这一点，这很正常，世界总是这样。
    <li>发现比传统测试方式更多的Bug。
    <li>使IDE的调试功能失去意义，或者应该说，避免了令人头痛的调试和节约了调试的时间。
    <li>总是处在要么编程要么重构的状态下，不会使人抓狂。（两顶帽子）
    <li>单元测试非常有趣。 </li>
</ol>
<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>&nbsp;</td>
            <td align=middle>（可运行）</td>
            <td>&nbsp;</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><strong>[什么时候重构？]</strong> <br>如果您在软件公司工作，就意味着您成天都会和想通过重构改善代码质量的想法打交道，不仅您如此，您的大部分同事也都如此。可是，究竟什么时候该重构，什么情况下应该重构呢？我相信您和您的同事可能有很多不同的看法，最常见的答案是&#8220;该重构时重构&#8221;，&#8220;写不下去的时候重构&#8221;，和&#8220;下一次迭代开始之前重构&#8221;，或者干脆就是&#8220;最近没时间，就不重构了，下次有时间的时候重构吧&#8221;。正如您已经预见到我想说的——这些想法都是对重构的误解。重构不是一种构建软件的工具，不是一种设计软件的模式，也不是一个软件开发过程中的环节，正确理解重构的人应该把重构看成一种书写代码的方式，或习惯，重构时时刻刻有可能发生。在TDD中，除去编写测试用例和实现测试用例之外的所有工作都是重构，所以，没有重构任何设计都不能实现。至于什么时候重构嘛，还要分开看，有三句话是我的经验：实现测试用例时重构代码，完成某个特性时重构设计，产品的重构完成后还要记得重构一下测试用例哦。</p>
<p><strong>[什么时候设计？]</strong> <br>这个问题比前面一个要难回答的多，实话实说，本人在依照TDD开发软件的时候也常常被这个问题困扰，总是觉得有些问题应该在写测试用例之前定下来，而有些问题应该在新增一个一个测试用例的过程中自然出现，水到渠成。所以，我的建议是，设计的时机应该由开发者自己把握，不要受到TDD方式的限制，但是，不需要事先确定的事一定不能事先确定，免得捆住了自己的手脚。</p>
<p><strong>[什么时候增加新的TestCase？]</strong> <br>没事做的时候。通常我们认为，如果你要增加一个新的功能，那么先写一个不能通过的 TestCase；如果你发现了一个bug，那么先写一个不能通过的TestCase；如果你现在什么都没有，从0开始，请先写一个不能通过的 TestCase。所有的工作都是从一个TestCase开始。此外，还要注意的是，一些大师要求我们每次只允许有一个TestCase亮红灯，在这个 TestCase没有Green之前不可以写别的TestCase，这种要求可以适当考虑，但即使有多个TestCase亮红灯也不要紧，并未违反TDD 的主要精神。</p>
<p><strong>[TestCase该怎么写？]</strong> <br>测试用例的编写实际上就是两个过程：使用尚不存在的代码和定义这些代码的执行结果。所以一个 TestCase也就应该包括两个部分——场景和断言。第一次写TestCase的人会有很大的不适应的感觉，因为你之前所写的所有东西都是在解决问题，现在要你提出问题确实不大习惯，不过不用担心，你正在做正确的事情，而这个世界上最难的事情也不在于如何解决问题，而在于ask the right question！</p>
<p><strong>[TDD能帮助我消除Bug吗？]</strong> <br>答：不能！千万不要把&#8220;测试&#8221;和&#8220;除虫&#8221;混为一谈！&#8220;除虫&#8221;是指程序员通过自己的努力来减少bug的数量（消除bug这样的字眼我们还是不要讲为好^_^），而&#8220;测试&#8221;是指程序员书写产品以外的一段代码来确保产品能有效工作。虽然TDD所编写的测试用例在一定程度上为寻找bug提供了依据，但事实上，按照TDD的方式进行的软件开发是不可能通过TDD再找到bug的（想想我们前面说的&#8220;完工时完工&#8221;），你想啊，当我们的代码完成的时候，所有的测试用例都亮了绿灯，这时隐藏在代码中的bug一个都不会露出马脚来。</p>
<p>但是，如果要问&#8220;测试&#8221;和&#8220;除虫&#8221;之间有什么联系，我相信还是有很多话可以讲的，比如TDD事实上减少了bug的数量，把查找bug战役的关注点从全线战场提升到代码战场以上。还有，bug的最可怕之处不在于隐藏之深，而在于满天遍野。如果你发现了一个用户很不容易才能发现的bug，那么不一定对工作做出了什么杰出贡献，但是如果你发现一段代码中，bug的密度或离散程度过高，那么恭喜你，你应该抛弃并重写这段代码了。TDD避免了这种情况，所以将寻找bug的工作降低到了一个新的低度。</p>
<p><strong>[我该为一个Feature编写TestCase还是为一个类编写TestCase？]</strong> <br>初学者常问的问题。虽然我们从TDD 的说明书上看到应该为一个特性编写相应的TestCase，但为什么著名的TDD大师所写的TestCase都是和类/方法一一对应的呢？为了解释这个问题，我和我的同事们都做了很多试验，最后我们得到了一个结论，虽然我不知道是否正确，但是如果您没有答案，可以姑且相信我们。</p>
<p>我们的研究结果表明，通常在一个特性的开发开始时，我们针对特性编写测试用例，如果您发现这个特性无法用TestCase表达，那么请将这个特性细分，直至您可以为手上的特性写出TestCase为止。从这里开始是最安全的，它不会导致任何设计上重大的失误。但是，随着您不断的重构代码，不断的重构 TestCase，不断的依据TDD的思想做下去，最后当产品伴随测试用例集一起发布的时候，您就会不经意的发现经过重构以后的测试用例很可能是和产品中的类/方法一一对应的。</p>
<p><strong>[什么时候应该将全部测试都运行一遍？]</strong> <br>Good Question！大师们要求我们每次重构之后都要完整的运行一遍测试用例。这个要求可以理解，因为重构很可能会改变整个代码的结构或设计，从而导致不可预见的后果，但是如果我正在开发的是一个ERP怎么办？运行一遍完整的测试用例可能将花费数个小时，况且现在很多重构都是由工具做到的，这个要求的可行性和前提条件都有所动摇。所以我认为原则上你可以挑几个你觉得可能受到本次重构影响的TestCase去run，但是如果运行整个测试包只要花费数秒的时间，那么不介意你按大师的要求去做。</p>
<p><strong>[什么时候改进一个TestCase？]</strong> <br>增加的测试用例或重构以后的代码导致了原来的TestCase的失去了效果，变得无意义，甚至可能导致错误的结果，这时是改进TestCase的最好时机。但是有时你会发现，这样做仅仅导致了原来的TestCase在设计上是臃肿的，或者是冗余的，这都不要紧，只要它没有失效，你仍然不用去改进它。记住，TestCase不是你的产品，它不要好看，也不要怎么太科学，甚至没有性能要求，它只要能完成它的使命就可以了——这也证明了我们后面所说的&#8220;用Ctrl-C/Ctrl-V编写测试用例&#8221;的可行性。</p>
<p>但是，美国人的想法其实跟我们还是不太一样，拿托尼巴赞的MindMap来说吧，其实画MindMap只是为了表现自己的思路，或记忆某些重要的事情，但托尼却建议大家把MindMap画成一件艺术品，甚至还有很多艺术家把自己画的抽象派MindMap拿出来帮助托尼做宣传。同样，大师们也要求我们把TestCase写的跟代码一样质量精良，可我想说的是，现在国内有几个公司能把产品的代码写的精良？？还是一步一步慢慢来吧。</p>
<p><strong>[为什么原来通过的测试用例现在不能通过了？]</strong> <br>这是一个警报，Red Alert！它可能表达了两层意思——都不是什么好意思——1）你刚刚进行的重构可能失败了，或存在一些错误未被发现，至少重构的结果和原来的代码不等价了。2）你刚刚增加的TestCase所表达的意思跟前面已经有的TestCase相冲突，也就是说，新增的功能违背了已有的设计，这种情况大部分可能是之前的设计错了。但无论哪错了，无论是那层意思，想找到这个问题的根源都比TDD的正常工作要难。</p>
<p><strong>[我怎么知道那里该有一个方法还是该有一个类？]</strong> <br>这个问题也是常常出现在我的脑海中，无论你是第一次接触TDD或者已经成为 TDD专家，这个问题都会缠绕着你不放。不过问题的答案可以参考前面的&#8220;什么时候设计&#8221;一节，答案不是唯一的。其实多数时候你不必考虑未来，今天只做今天的事，只要有重构工具，从方法到类和从类到方法都很容易。</p>
<p><strong>[我要写一个TestCase，可是不知道从哪里开始？]</strong> <br>从最重要的事开始，what matters most？从脚下开始，从手头上的工作开始，从眼前的事开始。从一个没有UI的核心特性开始，从算法开始，或者从最有可能耽误时间的模块开始，从一个最严重的bug开始。这是TDD主义者和鼠目寸光者的一个共同点，不同点是前者早已成竹在胸。</p>
<p><strong>[为什么我的测试总是看起来有点愚蠢？]</strong> <br>哦？是吗？来，握个手，我的也是！不必担心这一点，事实上，大师们给的例子也相当愚蠢，比如一个极端的例子是要写一个两个int变量相加的方法，大师先断言2+3=5，再断言5+5=10，难道这些代码不是很愚蠢吗？其实这只是一个极端的例子，当你初次接触TDD时，写这样的代码没什么不好，以后当你熟练时就会发现这样写没必要了，要记住，谦虚是通往TDD的必经之路！从经典开发方法转向TDD就像从面向过程转向面向对象一样困难，你可能什么都懂，但你写出来的类没有一个纯OO的！我的同事还告诉我真正的太极拳，其速度是很快的，不比任何一个快拳要慢，但是初学者（通常是指学习太极拳的前10年）太不容易把每个姿势都做对，所以只能慢慢来。</p>
<p><strong>[什么场合不适用TDD？]</strong> <br>问的好，确实有很多场合不适合使用TDD。比如对软件质量要求极高的军事或科研产品——神州六号，人命关天的软件——医疗设备，等等，再比如设计很重要必须提前做好的软件，这些都不适合TDD，但是不适合TDD不代表不能写TestCase，只是作用不同，地位不同罢了。</p>
<p><font color=#ff0000 size=5>{Best Practise}</font> </p>
<p><strong>[微笑面对编译错误]</strong> <br>学生时代最害怕的就是编译错误，编译错误可能会被老师视为上课不认真听课的证据，或者同学间相互嘲笑的砝码。甚至离开学校很多年的老程序员依然害怕它就像害怕迟到一样，潜意识里似乎编译错误极有可能和工资挂钩（或者和智商挂钩，反正都不是什么好事）。其实，只要提交到版本管理的代码没有编译错误就可以了，不要担心自己手上的代码的编译错误，通常，编译错误都集中在下面三个方面：<br>（1）你的代码存在低级错误<br>（2）由于某些Interface的实现尚不存在，所以被测试代码无法编译<br>（3）由于某些代码尚不存在，所以测试代码无法编译<br>请注意第二点与第三点完全不同，前者表明设计已存在，而实现不存在导致的编译错误；后者则指仅有TestCase而其它什么都没有的情况，设计和实现都不存在，没有Interface也没有Implementation。</p>
<p>另外，编译器还有一个优点，那就是以最敏捷的身手告诉你，你的代码中有那些错误。当然如果你拥有Eclipse这样可以及时提示编译错误的IDE，就不需要这样的功能了。</p>
<p><strong>[重视你的计划清单]</strong> <br>在非TDD的情况下，尤其是传统的瀑布模型的情况下，程序员不会不知道该做什么，事实上，总是有设计或者别的什么制品在引导程序员开发。但是在TDD的情况下，这种优势没有了，所以一个计划清单对你来说十分重要，因为你必须自己发现该做什么。不同性格的人对于这一点会有不同的反应，我相信平时做事没什么计划要依靠别人安排的人（所谓将才）可能略有不适应，不过不要紧，Tasks和Calendar（又称效率手册）早已成为现代上班族的必备工具了；而平时工作生活就很有计划性的人，比如我:)，就会更喜欢这种自己可以掌控Plan的方式了。</p>
<p><strong>[废黜每日代码质量检查]</strong> <br>如果我没有记错的话，PSP对于个人代码检查的要求是蛮严格的，而同样是在针对个人的问题上， TDD却建议你废黜每日代码质量检查，别起疑心，因为你总是在做TestCase要求你做的事情，并且总是有办法（自动的）检查代码有没有做到这些事情 ——红灯停绿灯行，所以每日代码检查的时间可能被节省，对于一个严格的PSP实践者来说，这个成本还是很可观的！</p>
<p>此外，对于每日代码质量检查的另一个好处，就是帮助你认识自己的代码，全面的从宏观、微观、各个角度审视自己的成果，现在，当你依照TDD做事时，这个优点也不需要了，还记得前面说的TDD的第二个优点吗，因为你已经全面的使用了一遍你的代码，这完全可以达到目的。</p>
<p>但是，问题往往也并不那么简单，现在有没有人能告诉我，我如何全面审视我所写的测试用例呢？别忘了，它们也是以代码的形式存在的哦。呵呵，但愿这个问题没有把你吓到，因为我相信到目前为止，它还不是瓶颈问题，况且在编写产品代码的时候你还是会自主的发现很多测试代码上的没考虑到的地方，可以就此修改一下。道理就是如此，世界上没有任何方法能代替你思考的过程，所以也没有任何方法能阻止你犯错误，TDD仅能让你更容易发现这些错误而已。</p>
<p><strong>[如果无法完成一个大的测试，就从最小的开始]</strong> <br>如果我无法开始怎么办，教科书上有个很好的例子：我要写一个电影列表的类，我不知道如何下手，如何写测试用例，不要紧，首先想象静态的结果，如果我的电影列表刚刚建立呢，那么它应该是空的，OK，就写这个断言吧，断言一个刚刚初始化的电影列表是空的。这不是愚蠢，这是细节，奥运会五项全能的金牌得主玛丽莲&#183;金是这样说的：&#8220;成功人士的共同点在于&#8230;&#8230;如果目标不够清晰，他们会首先做通往成功道路上的每一个细小步骤&#8230;&#8230;&#8221;。</p>
<p><strong>[尝试编写自己的xUnit]</strong> <br>Kent Beck建议大家每当接触一个新的语言或开发平台的时候，就自己写这个语言或平台的xUnit，其实几乎所有常用的语言和平台都已经有了自己的 xUnit，而且都是大同小异，但是为什么大师给出了这样的建议呢。其实Kent Beck的意思是说通过这样的方式你可以很快的了解这个语言或平台的特性，而且xUnit确实很简单，只要知道原理很快就能写出来。这对于那些喜欢自己写底层代码的人，或者喜欢控制力的人而言是个好消息。</p>
<p><strong>[善于使用Ctrl-C/Ctrl-V来编写TestCase]</strong> <br>不必担心TestCase会有代码冗余的问题，让它冗余好了。</p>
<p><strong>[永远都是功能First，改进可以稍后进行]</strong> <br>上面这个标题还可以改成另外一句话：避免过渡设计！</p>
<p><strong>[淘汰陈旧的用例]</strong> <br>舍不得孩子套不着狼。不要可惜陈旧的用例，因为它们可能从概念上已经是错误的了，或仅仅会得出错误的结果，或者在某次重构之后失去了意义。当然也不一定非要删除它们，从TestSuite中除去（JUnit）或加上Ignored（NUnit）标签也是一个好办法。</p>
<p><strong>[用TestCase做试验]</strong> <br>如果你在开始某个特性或产品的开发之前对某个领域不太熟悉或一无所知，或者对自己在该领域里的能力一无所知，那么你一定会选择做试验，在有单元测试作工具的情况下，建议你用TestCase做试验，这看起来就像你在写一个验证功能是否实现的 TestCase一样，而事实上也一样，只不过你所验证的不是代码本身，而是这些代码所依赖的环境。</p>
<p><strong>[TestCase之间应该尽量独立]</strong> <br>保证单独运行一个TestCase是有意义的。</p>
<p><strong>[不仅测试必须要通过的代码，还要测试必须不能通过的代码]</strong> <br>这是一个小技巧，也是不同于设计思路的东西。像越界的值或者乱码，或者类型不符的变量，这些输入都可能会导致某个异常的抛出，或者导致一个标示&#8220;illegal parameters&#8221;的返回值，这两种情况你都应该测试。当然我们无法枚举所有错误的输入或外部环境，这就像我们无法枚举所有正确的输入和外部环境一样，只要TestCase能说明问题就可以了。</p>
<p><strong>[编写代码的第一步，是在TestCase中用Ctrl-C]</strong> <br>这是一个高级技巧，呃，是的，我是这个意思，我不是说这个技巧难以掌握，而是说这个技巧当且仅当你已经是一个TDD高手时，你才能体会到它的魅力。多次使用TDD的人都有这样的体会，既然我的TestCase已经写的很好了，很能说明问题，为什么我的代码不能从TestCase拷贝一些东西来呢。当然，这要求你的TestCase已经具有很好的表达能力，比如断言f (5)=125的方式显然没有断言f(5)=5^(5-2)表达更多的内容。</p>
<p><strong>[测试用例包应该尽量设计成可以自动运行的]</strong> <br>如果产品是需要交付源代码的，那我们应该允许用户对代码进行修改或扩充后在自己的环境下run整个测试用例包。既然通常情况下的产品是可以自动运行的，那为什么同样作为交付用户的制品，测试用例包就不是自动运行的呢？即使产品不需要交付源代码，测试用例包也应该设计成可以自动运行的，这为测试部门或下一版本的开发人员提供了极大的便利。</p>
<p><strong>[只亮一盏红灯]</strong> <br>大师的建议，前面已经提到了，仅仅是建议。</p>
<p><strong>[用TestCase描述你发现的bug]</strong> <br>如果你在另一个部门的同事使用了你的代码，并且，他发现了一个bug，你猜他会怎么做？他会立即走到你的工位边上，大声斥责说：&#8220;你有bug！&#8221;吗？如果他胆敢这样对你，对不起，你一定要冷静下来，不要当面回骂他，相反你可以微微一笑，然后心平气和的对他说：&#8220;哦，是吗？那么好吧，给我一个TestCase证明一下。&#8221;现在局势已经倒向你这一边了，如果他还没有准备好回答你这致命的一击，我猜他会感到非常羞愧，并在内心责怪自己太莽撞。事实上，如果他的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所说：&#8220;软件工程有史以来从没有如此众多的人大大收益于如此简单的代码！&#8221;而且多数语言和平台的xUnit架构都是大同小异，有的仅是语言不同，其中最有代表性的是JUnit和NUnit，后者是前者的创新和扩展。一个单元测试框架xUnit应该：1）使每个TestCase独立运行；2）使每个TestCase可以独立检测和报告错误；3）易于在每次运行之前选择TestCase。下面是我枚举出的xUnit框架的概念，这些概念构成了当前业界单元测试理论和工具的核心：</p>
<p><strong>[测试方法/TestMethod]</strong> <br>测试的最小单位，直接表示为代码。</p>
<p><strong>[测试用例/TestCase]</strong> <br>由多个测试方法组成，是一个完整的对象，是很多TestRunner执行的最小单位。</p>
<p><strong>[测试容器/TestSuite]</strong> <br>由多个测试用例构成，意在把相同含义的测试用例手动安排在一起，TestSuite可以呈树状结构因而便于管理。在实现时，TestSuite形式上往往也是一个TestCase或TestFixture。</p>
<p><strong>[断言/Assertion]</strong> <br>断言一般有三类，分别是比较断言（如assertEquals），条件断言（如isTrue），和断言工具（如fail）。</p>
<p><strong>[测试设备/TestFixture]</strong> <br>为每个测试用例安排一个SetUp方法和一个TearDown方法，前者用于在执行该测试用例或该用例中的每个测试方法前调用以初始化某些内容，后者在执行该测试用例或该用例中的每个方法之后调用，通常用来消除测试对系统所做的修改。</p>
<p><strong>[期望异常/Expected Exception]</strong> <br>期望该测试方法抛出某种指定的异常，作为一个&#8220;断言&#8221;内容，同时也防止因为合情合理的异常而意外的终止了测试过程。</p>
<p><strong>[种类/Category]</strong> <br>为测试用例分类，实际使用时一般有TestSuite就不再使用Category，有Category就不再使用TestSuite。</p>
<p><strong>[忽略/Ignored]</strong> <br>设定该测试用例或测试方法被忽略，也就是不执行的意思。有些被抛弃的TestCase不愿删除，可以定为Ignored。</p>
<p><strong>[测试执行器/TestRunner]</strong> <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: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><span style="COLOR: rgb(0,0,255)">public</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">void</span> <span style="COLOR: rgb(0,0,0)">&nbsp;testFab()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;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: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><span style="COLOR: rgb(0,0,255)">public</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;fib(&nbsp;</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;n&nbsp;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">return</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</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: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><span style="COLOR: rgb(0,0,255)">public</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">void</span> <span style="COLOR: rgb(0,0,0)">&nbsp;testFab()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">2</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">2</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;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: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><span style="COLOR: rgb(0,0,255)">public</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">void</span> <span style="COLOR: rgb(0,0,0)">&nbsp;testFab()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">2</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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)">),&nbsp;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: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><span style="COLOR: rgb(0,0,255)">public</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;fib(&nbsp;</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;n&nbsp;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">if</span> <span style="COLOR: rgb(0,0,0)">&nbsp;(&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">==</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">3</span> <span style="COLOR: rgb(0,0,0)">&nbsp;)&nbsp;</span> <span style="COLOR: rgb(0,0,255)">return</span> <span style="COLOR: rgb(0,0,0)">&nbsp;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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">return</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</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: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><span style="COLOR: rgb(0,0,255)">public</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">void</span> <span style="COLOR: rgb(0,0,0)">&nbsp;testFab()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">2</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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)">),&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">3</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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)">),&nbsp;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: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><span style="COLOR: rgb(0,0,255)">public</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;fib(&nbsp;</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;n&nbsp;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">if</span> <span style="COLOR: rgb(0,0,0)">&nbsp;(&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">==</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">3</span> <span style="COLOR: rgb(0,0,0)">&nbsp;)&nbsp;</span> <span style="COLOR: rgb(0,0,255)">return</span> <span style="COLOR: rgb(0,0,0)">&nbsp;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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">if</span> <span style="COLOR: rgb(0,0,0)">&nbsp;(&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">==</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">4</span> <span style="COLOR: rgb(0,0,0)">&nbsp;)&nbsp;</span> <span style="COLOR: rgb(0,0,255)">return</span> <span style="COLOR: rgb(0,0,0)">&nbsp;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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">return</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</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: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><span style="COLOR: rgb(0,0,255)">public</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;fib(&nbsp;</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;n&nbsp;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">if</span> <span style="COLOR: rgb(0,0,0)">&nbsp;(&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">==</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">||</span> <span style="COLOR: rgb(0,0,0)">&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">==</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">2</span> <span style="COLOR: rgb(0,0,0)">&nbsp;）&nbsp;</span> <span style="COLOR: rgb(0,0,255)">return</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">else</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">return</span> <span style="COLOR: rgb(0,0,0)">&nbsp;fib(&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">-</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">&nbsp;)&nbsp;</span> <span style="COLOR: rgb(0,0,0)">+</span> <span style="COLOR: rgb(0,0,0)">&nbsp;fib(&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">-</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">2</span> <span style="COLOR: rgb(0,0,0)">&nbsp;);<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: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><span style="COLOR: rgb(0,0,255)">public</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">void</span> <span style="COLOR: rgb(0,0,0)">&nbsp;testFab()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">2</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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)">),&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">3</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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)">),&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">4</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">0</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;fib(</span> <span style="COLOR: rgb(0,0,0)">0</span> <span style="COLOR: rgb(0,0,0)">));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span> <span style="COLOR: rgb(0,0,0)">0</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;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: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><span style="COLOR: rgb(0,0,255)">public</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;fib(&nbsp;</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;n&nbsp;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">if</span> <span style="COLOR: rgb(0,0,0)">&nbsp;(&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">&lt;=</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">0</span> <span style="COLOR: rgb(0,0,0)">&nbsp;)&nbsp;</span> <span style="COLOR: rgb(0,0,255)">return</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">0</span> <span style="COLOR: rgb(0,0,0)">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">if</span> <span style="COLOR: rgb(0,0,0)">&nbsp;(&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">==</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">||</span> <span style="COLOR: rgb(0,0,0)">&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">==</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">2</span> <span style="COLOR: rgb(0,0,0)">&nbsp;）&nbsp;</span> <span style="COLOR: rgb(0,0,255)">return</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">else</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">return</span> <span style="COLOR: rgb(0,0,0)">&nbsp;fib(&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">-</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">&nbsp;)&nbsp;</span> <span style="COLOR: rgb(0,0,0)">+</span> <span style="COLOR: rgb(0,0,0)">&nbsp;fib(&nbsp;n&nbsp;</span> <span style="COLOR: rgb(0,0,0)">-</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">2</span> <span style="COLOR: rgb(0,0,0)">&nbsp;);<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: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial">
<div><span style="COLOR: rgb(0,0,255)">public</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,255)">void</span> <span style="COLOR: rgb(0,0,0)">&nbsp;testFab()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;cases[][]&nbsp;</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,0)">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</span> <span style="COLOR: rgb(0,0,0)">0</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;</span> <span style="COLOR: rgb(0,0,0)">0</span> <span style="COLOR: rgb(0,0,0)">},&nbsp;{</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)">,&nbsp;</span> <span style="COLOR: rgb(0,0,0)">0</span> <span style="COLOR: rgb(0,0,0)">},&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,128,0)">//</span> <span style="COLOR: rgb(0,128,0)">the&nbsp;wrong&nbsp;parameters</span> <span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">},&nbsp;{</span> <span style="COLOR: rgb(0,0,0)">2</span> <span style="COLOR: rgb(0,0,0)">,&nbsp;</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">}};&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,128,0)">//</span> <span style="COLOR: rgb(0,128,0)">the&nbsp;first&nbsp;2&nbsp;elements</span> <span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">for</span> <span style="COLOR: rgb(0,0,0)">&nbsp;(</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;i&nbsp;</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">0</span> <span style="COLOR: rgb(0,0,0)">;&nbsp;i&nbsp;</span> <span style="COLOR: rgb(0,0,0)">&lt;</span> <span style="COLOR: rgb(0,0,0)">&nbsp;cases.length;&nbsp;i</span> <span style="COLOR: rgb(0,0,0)">++</span> <span style="COLOR: rgb(0,0,0)">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(&nbsp;cases[i][</span> <span style="COLOR: rgb(0,0,0)">1</span> <span style="COLOR: rgb(0,0,0)">],&nbsp;fib(cases[i][</span> <span style="COLOR: rgb(0,0,0)">0</span> <span style="COLOR: rgb(0,0,0)">])&nbsp;);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,128,0)">//</span> <span style="COLOR: rgb(0,128,0)">the&nbsp;rest&nbsp;elements</span> <span style="COLOR: rgb(0,128,0)"><br></span><span style="COLOR: rgb(0,0,0)">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: rgb(0,0,255)">for</span> <span style="COLOR: rgb(0,0,0)">&nbsp;(</span> <span style="COLOR: rgb(0,0,255)">int</span> <span style="COLOR: rgb(0,0,0)">&nbsp;i&nbsp;</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">3</span> <span style="COLOR: rgb(0,0,0)">;&nbsp;i&nbsp;</span> <span style="COLOR: rgb(0,0,0)">&lt;</span> <span style="COLOR: rgb(0,0,0)">&nbsp;</span> <span style="COLOR: rgb(0,0,0)">20</span> <span style="COLOR: rgb(0,0,0)">;&nbsp;i</span> <span style="COLOR: rgb(0,0,0)">++</span> <span style="COLOR: rgb(0,0,0)">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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)">),&nbsp;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><span style="COLOR: #0000ff">Martin Fowler：当然（值得花一半的时间来写单元测试）！因为单元测试能够使你更快的完成工作。无数次的实践已经证明这一点。你的时间越是紧张，就越要写单元测试，它看上去慢，但实际上能够帮助你更快、更舒服地达到目的。<br>Martin Fowler：什么叫重要？什么叫不重要？这是需要逐渐认识的，不是想当然的。我为绝大多数的模块写单元测试，是有点烦人，但是当你意识到这工作的价值时，你会欣然的。<br>Martin Fowler：对全世界的程序员我都是那么几条建议：&#8230;&#8230;第二，学习测试驱动开发，这种新的方法会改变你对于软件开发的看法。&#8230;&#8230;</span> </p>
<p align=right>——《程序员》，2005年7月刊</p>
</blockquote>
<p><font color=#ff0000 size=5>{鸣谢}</font> </p>
<p><a href="http://www.blogjava.net/fhawk"><u><font color=#0000ff>fhawk</font></u></a> <br>Dennis Chen<br><a href="http://xdingding.cnblogs.com/"><font color=#0000ff><u>般若菩提</u></font></a><br><a href="http://c2.com/ppr/about/author/kent.html"><font color=#0000ff><u>Kent Beck</u></font></a><br><a href="http://www.martinfowler.com/"><font color=#0000ff><u>Martin Fowler</u></font></a><br><a href="http://c2.com/"><u><font color=#0000ff>c2.com</font></u></a></p>
<img src ="http://www.cppblog.com/shuiyuan2004/aggbug/45751.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shuiyuan2004/" target="_blank">不倦</a> 2008-03-30 16:42 <a href="http://www.cppblog.com/shuiyuan2004/archive/2008/03/30/45751.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Boost Test Library</title><link>http://www.cppblog.com/shuiyuan2004/archive/2008/03/29/45677.html</link><dc:creator>不倦</dc:creator><author>不倦</author><pubDate>Sat, 29 Mar 2008 07:39:00 GMT</pubDate><guid>http://www.cppblog.com/shuiyuan2004/archive/2008/03/29/45677.html</guid><wfw:comment>http://www.cppblog.com/shuiyuan2004/comments/45677.html</wfw:comment><comments>http://www.cppblog.com/shuiyuan2004/archive/2008/03/29/45677.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/shuiyuan2004/comments/commentRss/45677.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shuiyuan2004/services/trackbacks/45677.html</trackback:ping><description><![CDATA[<strong>boost 的 Test<br><br></strong>test 库中有如下的组件：<br><strong>Execution Monitor</strong>&nbsp; 一个基本用于 program 和 test program 的异常与错误检测与报告机制，Execution Monitor 调用用户提供的函数并报告所有捕获的运行时的异常，它只被其他 Boost Test Library components 内部调用，当然也可以用于一些 production environment 控制那些会导致程序崩溃的函数的调用；<br><br><strong>Program Execution Monitor</strong>&nbsp; 一个简单的 helper facility 用于监控一个程序的运行，Program Execution Monitor 提供了 main() 函数和 Execution Monitor 监控程序的执行，可以用以 production environment 产生一致错误报告，控制在 test environment 环境中运行的程序，直接使用 Test Execution Monitor；<br><br><strong>Test Tools</strong>&nbsp; 一个用以进行 testing 的一个 toolbox，Test Tools 被用来测试在 Test Execution Monitor 或 Unit Test Framework 控制下运行的程序；<br><br><strong>Test Execution Monitor</strong>&nbsp; 让一个测试程序在 monitored environment 环境中运行，Test Execution Monitor 提供了 main() 来控制被测试程序的运行并可以让 Test Tools 实现测试的逻辑，它被用在 test environment，如果要控制 production code 的运行使用 Program Execution Monitor；<br><br><strong>Unit Test Framework</strong>&nbsp; 用以简化编写和组织 test cases 的 framework，支持简单函数或者是成员函数编写的 test cases 并将他们组织成一个 test suites 的 tree，该 framework 使用 Test Tools 来实现 test cases 并提供了一种机制用以管理 log report level 和 result report level；<br><br><strong>minimal testing facility</strong>&nbsp; 提供 Boost Test 最初版本的提供的功能的简单 facility，提供了和 Test Execution Monitor 一样的机制，但额外定义了一些简单提供 Test Tools 类似功能的 test tools，它不需要和任何的外部组件 link，适合简单和快速测试的需要，使用于 test environment。<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!146.entry)<br><br><br><strong>boost Test 的 Execution Monitor</strong><br><br>使用 Execution Monitor 的三部曲：<br>1. #include &lt;boost/test/execution_monitor.hpp&gt;<br>2. Make an instance of boost::execution_monitor<br>3. Optionally register custom exception translators for exception classes you want special processing<br>&nbsp;<br>调用 execution_monitor::execute( function_to_monitor, catch_system_exception, timeout ) 运行 monitored function。如果调用成功则返回一个 integer value，如果有如下的事情发生<br>1. Uncaught C++ exception<br>2. hardware or software signal, trap, or other exception<br>3. Timeout reached<br>4. debug assert event occurred (under Microsoft Visual C++ or compatible compiler)<br>the method execution_monitor::execute( ... ) throws the boost::execution_exception<br>如果希望程序 error message 被转化为&nbsp; execution_exception 的 error message，则扔出如下的三类异常：C string，std:string，any exception class in std::exception hierarchy。<br>&nbsp;<br>终止 monitored function 而不让 Execution Monitor 报告 any error 的最佳方法是抛出 boost::execution_aborted。如果不喜欢 "unknown exception caught" message 而更愿意使用自定义的 exception，可以向 execution monitor 为 any exception types 注册 translator 函数，如<br>ex_mon.register_exception_translator&lt;my_exception1&gt;( &amp;translate_my_exception1 );<br>my_exception1 是异常类型，translate_my_exception1 是异常处理函数。<br>class execution_monitor {<br>public:<br>&nbsp;&nbsp;&nbsp; virtual&nbsp;&nbsp;&nbsp;&nbsp; ~execution_monitor();<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; template&lt;typename Exception, typename ExceptionTranslator&gt;<br>&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; register_exception_translator( ExceptionTranslator const&amp; tr, boost::type&lt;Exception&gt;* = 0 );<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; execute( unit_test::callback0&lt;int&gt; const&amp; F, bool catch_system_errors = true, int timeout = 0 );<br>}; // exception monitor<br>&nbsp;<br>Execution Monitor 用 boost::execution_exception 报告捕获的问题，其最大的特点是不分配任何 memory 因此在内存稀缺环境中使用。<br>class execution_exception {<br>public:<br>&nbsp;&nbsp;&nbsp; execution_exception( error_code ec, const_string what_msg );<br>&nbsp;&nbsp;&nbsp; enum error_code {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cpp_exception_error,&nbsp;&nbsp;&nbsp; // see note (1) below<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user_error,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // user reported nonfatal error<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; system_error,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // see note (2) below<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout_error,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // only detectable on certain platforms<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user_fatal_error,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // user reported fatal error<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; system_fatal_error&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // see note (2) below<br>&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp; error_code&nbsp;&nbsp; code() const;&nbsp; // use this method to get an error code for the exception<br>&nbsp;&nbsp;&nbsp; const_string what() const;&nbsp; // use this method to get an error message for the exception<br>};<br>Note 1 ：uncaught C++ exceptions 被当做 error，如果应用程序捕获到 C++ exception，则该 exception 不会到 boost::execution_monitor；<br>Note 2 ：这些 error 包括 UNIX signals 和 Windows structured exceptions，这些经常由 hardware traps 触发。<br><br>execution_monitor 可以动态连接，库中没有提供 main 函数。由于只有一个 libs/test/execution_monitor.cpp 文件，因此可以直接 copy 该文件。<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!148.entry)<br><br><br><strong>boost Test 的 Program Execution Monitor<br><br></strong>C++ program 可以通过 return value 和 throwing an exception 报告 user-detected errors，而如 dereferencing an invalid pointer 的 System-detected errors 则以其他方式报告。<br>&nbsp;<br>Boost Test Library 的 Program Execution Monitor 减轻了用户处理复杂的 error detection 和 reporting，并提供了 main() 函数在 monitored environment 来调用用户提供的 cpp_main() 函数，main() 函数以一致的方式检测和报告多种 errors 的发生，将其转化为一致的 return code 返回给 host enviroment。<br>BOOST_TEST_CATCH_SYSTEM_ERRORS 设置允许 Execution Monitor 捕获 system errors，默认值为 "yes"。<br>BOOST_PRG_MON_CONFIRM 设置是否允许用户交互确认程序正确运行，默认值为"yes"。<br>&nbsp;<br>Program Execution Monitor 使用 Execution Monitor 监视用户提供的 cpp_main() 函数的运行。尽管 Program Execution Monitor 在 libs/test/src/cpp_main.cpp 提供了 main() 函数，但是为了 link 正确，用户必须提供一个与 main() 函数一样接口的 cpp_main() 函数。如 cpp_main() 抛出异常或返回非 0 值，Program Execution Monitor 就认为程序发生错误。<br>Program Execution Monitor 在 cout 和 cerr 流上分别提供详细和简要的错误报告。Program Execution Monitor 提供的 main() 函数返回如下的值<br>1. boost::exit_success - no errors<br>2. boost::exit_failure - non-zero and non-boost::exit_success return code from cpp_main().<br>3. boost::exit_exception_failure - cpp_main() throw an exception.<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!149.entry)<br><br><br><strong>boost Test 的 Test Tools<br><br></strong>Test Tools 为了让用户使用方便，提供了一系列的宏，这些宏分三级，其效果是不同的：<br>WARN 不增加引用计数，继续执行程序<br>CHECK 增加应用计数，继续执行程序<br>REQUIRE 增加应用计数，中断程序的运行<br>&nbsp;<br>使用 CHECK level tools 实现 assertions，使用 WARN level tools 检验不太重要但是正确的方面，如：性能、可移植性、有用性，如 assertions 失败就不应该让程序继续运行则使用 REQUIRE level tools。<br>&nbsp;<br>Test Tools 提供了两个 .hpp 文件 boost/test/test_tools.hpp 和 boost/test/floating_point_comparison.hpp，一个 .cpp 文件 libs/test/test_tools.cpp<br>BOOST_WARN( P )<br>BOOST_CHECK( P )<br>BOOST_REQUIRE( P )<br>BOOST_WARN_MESSAGE( P, M )<br>BOOST_CHECK_MESSAGE( P, M )<br>BOOST_REQUIRE_MESSAGE( P, M )<br>BOOST_ERROR( M )BOOST_FAIL( M )<br>BOOST_MESSAGE( M )<br>BOOST_CHECKPOINT( M )<br>BOOST_WARN_THROW( S, E )<br>BOOST_CHECK_THROW( S, E )<br>BOOST_REQUIRE_THROW( S, E )<br>BOOST_WARN_EXCEPTION( S, E, P )<br>BOOST_CHECK_EXCEPTION( S, E, P )<br>BOOST_REQUIRE_EXCEPTION( S, E, P )<br>BOOST_IGNORE_CHECK( e )<br>BOOST_WARN_NO_THROW( S )<br>BOOST_CHECK_NO_THROW( S )<br>BOOST_REQUIRE_NO_THROW( S )<br>BOOST_WARN_CLOSE( L, R, T )<br>BOOST_CHECK_CLOSE( L, R, T )<br>BOOST_REQUIRE_CLOSE( L, R, T )<br>BOOST_WARN_SMALL( FPV, T )<br>BOOST_CHECK_SMALL( FPV, T )<br>BOOST_REQUIRE_SMALL( FPV, T )<br>BOOST_WARN_PREDICATE( P, ARGS )<br>BOOST_CHECK_PREDICATE( P, ARGS )<br>BOOST_REQUIRE_PREDICATE( P, ARGS )<br>BOOST_WARN_EQUAL_COLLECTIONS( L_begin, L_end, R_begin, R_end )<br>BOOST_CHECK_EQUAL_COLLECTIONS( L_begin, L_end, R_begin, R_end )<br>BOOST_REQUIRE_EQUAL_COLLECTIONS( L_begin, L_end, R_begin, R_end )<br>BOOST_WARN_BITWISE_EQUAL( L, R )<br>BOOST_CHECK_BITWISE_EQUAL( L, R )<br>BOOST_REQUIRE_BITWISE_EQUAL( L, R )<br>BOOST_IS_DEFINED( symb )<br>BOOST_BITWISE_EQUAL( L, R )<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!145.entry)<br><br><br><strong>boost Test 的 Test Execution Monitor<br><br></strong>Test Execution Monitor 结合了 Test Tools 和 Execution Monitor 的特点简化了烦琐测试工作，它提供了 main() 函数调用用户提供的 test_main() 函数，用户可以使用 Test Tools 来进行复杂的验证工作。<br>&nbsp;<br>Test Execution Monitor 被设计用来进行测试简单的程序或者从已存在的 production code 中 dig a problem。Program Execution Monitor 更适合监控 production (non-test) programs（因为它不影响程序的性能），而 Unit Test Framework 更适合 complex test programs，因为 Unit Test Framework 可以<br>1. 可以将 test 分割到多个 test cases，它会为每一个 test case 分别产生 pass/fail 的统计信息；<br>2. 假如某一个 test case 失败了不会影响其他的 test；<br>3. 可以通过指定 name 来运行特定的 test case<br>4. 分离的 test cases 运行更清楚发现特定测试模块的目的<br>5. 可以设置更多的选项<br>(http://yaekees.spaces.live.com/blog/cns!1955ee8c6707277a!150.entry)<br><br><br><strong>boost Test 的 Unit Test Framework<br><br></strong>regression testing 只关注程序运行的时候是否有错误发生，而 unit test 则需要尽可能详细的输出错误的信息。Unit Test Framework 用 test tools 简化了 test cases 的编写并将其组织为有层次的 test suites。它提供了 main() 函数初始化 framework，通过命令行参数或者是环境变量设置参数，并通过 init_unit_test_suite(argc, argv)，然后运行用户的 test suite。Framework 跟踪所有的 passed/failed 的 Test Tools assertions，可以通过 test cases 的数目（part 和 total）得到测试进度，并且可以多种形式提供结果。Unit Test Framework 可被用来进行简单测试和复杂重要测试，它不适合用在 production code，同时它以运行时的效率为代价加速编译。<br>&nbsp;<br>该函数负责创建和初始化顶层的 test_suite 实例，如 test_suite 创建失败该函数返回 NULL 指针，测试终止并返回一个 boost::exit_test_failure。Framework 在测试运行时传递特定的命令行参数，同时排斥其他 framework 指定的参数，同时 framework 负责 test_suite 的生命周期，在 test 终止时会被销毁。<br>&nbsp;<br>假如 test cases 会丢出自定义的异常，可以像 Execution Monitor 那样注册特定的 translator，注册函数的原型定义如下<br>template&lt;typename Exception, typename ExceptionTranslator&gt;<br>void boost::unit_test::register_exception_translator( ExceptionTranslator const&amp; tr, boost::type&lt;Exception&gt;* d = 0 ) <br>&nbsp;<br>一旦测试结束，framework 会报告结果并返回 return code。下面是集成在 unit test framework 内部的的返回值<br>boost::exit_success&nbsp; returned if no errors occurred during test or success result code was explicitly requested with the no result code framework parameter<br>boost::exit_test_failure&nbsp; returned if nonfatal errors detected and no uncaught exceptions thrown or the framework fails to initialize the test suite<br>boost::exit_exception_failure&nbsp; returned if fatal errors detected or uncaught exceptions thrown<br>&nbsp;<br>在 VC7.1+stlport 4.62 上 unit_test_example3.cpp 没有通过(对多种继承没有通过)<br>&nbsp;<br>简单的使用方法：<br>1. 首先定义 #define BOOST_AUTO_TEST_MAIN<br>2. 包含 #include &lt;boost/test/auto_unit_test.hpp&gt;<br>3. 创建一个 test_suite<br>BOOST_AUTO_TEST_CASE( test )<br>{<br>&nbsp;&nbsp;&nbsp; BOOST_CHECK( true );<br>}<br>然后 link libboost_test_exec_monitor-vc71-mt-sp-1_33.lib 就可以了。<br>&nbsp;<br>另一类使用方法，首先包含如下<br>#include &lt;boost/test/unit_test.hpp&gt;<br>#include &lt;boost/test/unit_test_monitor.hpp&gt;<br>using namespace boost::unit_test;<br>然后声明 test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) {}<br>一个典型用法是<br>test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) {<br>&nbsp;&nbsp;&nbsp; test_suite* test = BOOST_TEST_SUITE("custom_exception_test");<br>&nbsp;&nbsp;&nbsp; unit_test_monitor.register_exception_translator&lt;my_exception1&gt;( &amp;my_exception1_translator );<br>&nbsp;&nbsp;&nbsp; unit_test_monitor.register_exception_translator&lt;my_exception2&gt;( &amp;my_exception2_translator );<br>&nbsp;&nbsp;&nbsp; test-&gt;add( BOOST_TEST_CASE( &amp;throw_my_exception1 ) );<br>&nbsp;&nbsp;&nbsp; test-&gt;add( BOOST_TEST_CASE( &amp;throw_my_exception2 ) );<br>&nbsp;&nbsp;&nbsp; return test;<br>}<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!151.entry)<br><br><br><strong>boost 的 minimal testing facility<br><br></strong>只适合使用在 test environment 中，不需要 link 任何外部的组件。用户只需要提供了如下的函数<br>int test_main( int argc, char* argv[] ) 就可以了。minimal testing facility 提供了 BOOST_CHECK(predicate)，BOOST_REQUIRE(predicate)，BOOST_ERROR(message)， BOOST_FAIL(message)。除了这四个 MACRO 以外可以通过抛出异常和 return 返回值报告错误。下面是 boost 提供的一个示例。<br>#include &lt;boost/test/minimal.hpp&gt;<br>int add( int i, int j ) { return i+j; }<br>int test_main( int, char *[] )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // note the name!<br>{<br>&nbsp;&nbsp;&nbsp; // six ways to detect and report the same error:<br>&nbsp;&nbsp;&nbsp; BOOST_CHECK( add( 2,2 ) == 4 );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #1 continues on error<br>&nbsp;&nbsp;&nbsp; BOOST_REQUIRE( add( 2,2 ) == 4 );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #2 throws on error<br>&nbsp;&nbsp;&nbsp; if( add( 2,2 ) != 4 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BOOST_ERROR( "Ouch..." );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #3 continues on error<br>&nbsp;&nbsp;&nbsp; if( add( 2,2 ) != 4 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BOOST_FAIL( "Ouch..." );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #4 throws on error<br>&nbsp;&nbsp;&nbsp; if( add( 2,2 ) != 4 ) throw "Oops..."; // #5 throws on error<br>&nbsp;&nbsp;&nbsp; return add( 2, 2 ) == 4 ? 0 : 1;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // #6 returns error code<br>}<br>BOOST_CHECK 如果该表达式失败了，会出现源代码文件名、代码行号、并且会增加 error count，一旦程序终止 error count 会显示在 std::cout；<br>BOOST_REQUIRE 类似于 BOOST_CHECK，但是会抛出被&nbsp; Minimal testing facility 捕获的异常。<br>(http://yaekees.spaces.live.com/blog/cns!1955EE8C6707277A!147.entry)<br><br>
<img src ="http://www.cppblog.com/shuiyuan2004/aggbug/45677.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shuiyuan2004/" target="_blank">不倦</a> 2008-03-29 15:39 <a href="http://www.cppblog.com/shuiyuan2004/archive/2008/03/29/45677.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CxxTest使用指南（入门篇）</title><link>http://www.cppblog.com/shuiyuan2004/archive/2007/08/31/31322.html</link><dc:creator>不倦</dc:creator><author>不倦</author><pubDate>Fri, 31 Aug 2007 10:42:00 GMT</pubDate><guid>http://www.cppblog.com/shuiyuan2004/archive/2007/08/31/31322.html</guid><wfw:comment>http://www.cppblog.com/shuiyuan2004/comments/31322.html</wfw:comment><comments>http://www.cppblog.com/shuiyuan2004/archive/2007/08/31/31322.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/shuiyuan2004/comments/commentRss/31322.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shuiyuan2004/services/trackbacks/31322.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>CxxTest</span><span>使用指南（入门篇）</span></p>
<p><span>准备工作：</span></p>
<p><span><span>1、&nbsp;</span></span><span>安装</span><span>perl/Python</span></p>
<p><span><span>2、&nbsp;</span></span><span>下载解压</span><span>CxxTest</span></p>
<p><span><span>3、&nbsp;</span></span><span>设置环境变量：</span></p>
<p align=left><span>如果使用<span>Perl,</span>则设置一个名为<span>PERL</span>的环境变量，值为<span>perl.exe</span>的位置。（比如<span>D:\Perl\Bin\Perl.exe</span>）；</span></p>
<p align=left><span>如果安装<span>Python,</span>则设置一个名为<span>PYTHON</span>的环境变量，值为你安装的<span>Python</span>目录下的<span>python.exe</span>路径（比如<span>D:\Python25\python.exe</span>）；</span></p>
<p align=left><span>设置一个名为<span>CXXTESTDIR </span>的环境变量，值为<span>CxxTest</span>解压后的目录。（比如<span>D:\Cxxtest</span>）。（设置<span>CXXTESTDIR</span>的原因：免去每次在<span>makefile</span>文件中指定<span>CXXTESTDIR</span>的目录）</span></p>
<p align=left>&nbsp;</p>
<p align=left><span>方式一：使用<span>CxxTest</span></span><span> </span><span>\sample\msvc</span><span>中的框架</span></p>
<p align=left><span><span>1、&nbsp;</span></span><span>拷贝<span>msvc</span>文件夹到工程目录中</span></p>
<p align=left><span>打开<span>CxxTest_Workspace.sln</span>，可以看到三个项目：</span></p>
<p align=left><span>- CxxTest_3_Generate </span><span>运行</span><span> cxxtestgen.pl </span><span>生成</span><span> runner.cpp </span><span>文件；</span></p>
<p align=left><span>- CxxTest_2_Build </span><span>编译生成的</span><span>runner.cpp</span><span>文件；</span></p>
<p align=left><span>- CxxTest_1_Run </span><span>运行测试；</span></p>
<p align=left><span><span>2、&nbsp;</span></span><span>修改其中的<span>makefile</span>文件<span>:</span></span></p>
<p align=left><span>1</span><span>）将以下内容：</span></p>
<p align=left><span># Where to look for the tests</span></p>
<p align=left><span>TESTS<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>= ..\gui\*.h ..\*.h</span></p>
<p align=left><span>修改为：</span></p>
<p align=left><span># Where to look for the tests</span></p>
<p align=left><span>TESTS<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>= *.h</span></p>
<p align=left><span>这段话的作用是查找测试文件，我们的测试文件是<span>.h</span>格式的，放在当前目录下。（如果是<span>.hpp</span>格式的话，自然改成<span>*.hpp</span>；放在其它目录的话，还要修改路径）</span></p>
<p align=left><span>2</span><span>）将以下内容删除：</span></p>
<p align=left><span># Where the CxxTest distribution is unpacked</span></p>
<p align=left><span>CXXTESTDIR<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>= ..\..</span></p>
<p align=left><span>因为先前已经定义了<span>CXXTESTDIR</span>的环境变量，这里的定义可以省掉。</span></p>
<p align=left><span><span>3、&nbsp;</span></span><span>在</span><span>CxxTest_3_Generate</span><span>项目中添加测试文件</span></p>
<p align=left><span>这是测试文件的一个简单的例子：</span></p>
<p align=left><span>// Sampletest.h</span></p>
<p align=left><span>#include</span><span> <span>&lt;cxxtest/TestSuite.h&gt;</span></span></p>
<p align=left><span>//</span><span>定义一个测试套件类，将测试用例放入其中</span></p>
<p align=left><span>class</span><span> <span>SampletestSuite</span> : <span>public</span> <span>CxxTest</span>::<span>TestSuite</span><span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p align=left><span>{</span></p>
<p align=left><span>public</span><span>:</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span></span><span>定义测试，以<span>test</span>作为测试函数前缀，</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span></span><span>这是<span>cxxtestgen.pl</span>或<span>cxxtestgen.py</span>对测试文件进行扫描，抽取测试用例的依据</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>void</span> <span>testMultiplication</span>( <span>void</span> )<span>&nbsp;&nbsp;&nbsp; </span></span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>TS_ASSERT_EQUALS</span>( 2 * 2, 5 );</span></p>
<p align=left><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>}</span></p>
<p><span>};</span></p>
<p><span><span>4、&nbsp;</span></span><span>运行测试，即生成</span><span>CxxTest_1_Run</span><span>，可在输出窗口看到测试结果，并双击结果行可定位到源代码。</span></p>
<p>&nbsp;</p>
<p><span>方法二：简化<span>msvc</span>，将</span><span>Generate</span><span>，</span><span>Build</span><span>，</span><span>Run</span><span>集成到一个项目中</span></p>
<p><span><span>1、&nbsp;</span></span><span>在工程中添加新项目：</span><span>Unit</span><span>_<span>Test</span></span></p>
<p><span>将修改后的<span>makefile</span>文件拷贝到<span>Unit_Test</span>目录中。</span></p>
<p><span><span>2、&nbsp;</span></span><span>在<span>Unit_Test</span>项目源文件中添加新建项<span>dummy</span></span></p>
<p><span>无论添加进来的是<span>.cpp,</span>还是<span>.h,</span>将后缀名去掉。</span></p>
<p><span>右击<span>dummy</span>，选择属性，在弹出的属性页中，&#8220;自定义生成步骤&#8221;选项<span>-&gt;</span>&#8220;命令行&#8221;选项，输入：</span></p>
<p><span>ECHO Don't Delete this file!!! &gt; Dummy</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -------------------- Generating test cases --------------------</span></p>
<p><span>if exist runner.cpp <st1:place w:st="on"><st1:state w:st="on">del</st1:state></st1:place> runner.cpp /Q</span></p>
<p><span>NMAKE runner.cpp /nologo</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO &#8211;</span></p>
<p>&nbsp;</p>
<p><span>在&#8220;输出&#8221;选项中输入：</span></p>
<p><span>Runner.cpp</span></p>
<p>&nbsp;</p>
<p align=left><span>Dummy</span><span>只是一个文件，里边可以是任何内容，</span><span>关键点在于对<span>Dummy</span>文件进行编译的时候所执行的操作，这是一个批处理文件，这个批处理文件执行生成<span> testcase </span>的操作，也就是调用<span>Python/Perl</span>生成测试用例（也就是<span>runner.cpp</span>文件）。为了保持每次编译都可以生成新的<span>runner</span>文件，就必须保证</span></p>
<p align=left><span>1</span><span>）<span>Dummy</span>文件在<span>runner.cpp</span>之前进行编译；</span></p>
<p align=left><span>2</span><span>）<span>Dummy</span>文件每次都必须被重新编译。</span>&nbsp;</p>
<p align=left><span>第一点是通过文件顺序来的，第二点是在生成事件里边重新生成<span>Dummy</span>文件来保证的（<span>VS2005</span>似乎没问题，<span>VC6</span>似乎还是有些问题的）。</span></p>
<p align=left><span>从另外一个角度讲，这个<span>Dummy</span>文件相当于<span> samples/msvc </span>第一和第二个项目的功能。（即</span><span>Generate</span><span>和</span><span>Build</span><span>）</span></p>
<p align=left>&nbsp;</p>
<p><span><span>3、&nbsp;</span></span><span>编译<span>dummy</span>文件，生成<span>runner.cpp</span>，将其添加到<span>Unit_Test</span>&#8220;源文件&#8221;中。</span></p>
<p><span>确定当前目录中已有<span>.h</span>测试文件，否则可能提示错误。</span></p>
<p><span><span>4、&nbsp;</span></span><span>右击<span>Unit_Test</span>，选择属性，在弹出来的属性页中， &#8220;生成事件&#8221;选项<span>-&gt;</span>&#8220;生成后事件<span>&#8221;</span>选项<span>-&gt;</span>&#8220;命令行&#8221;选项中输入：</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -------------------- Running Unit Test Cases --------------------</span></p>
<p><span>$(OutDir)\$(TargetName).exe</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -</span></p>
<p><span>ECHO -------------------------------------------------------------------------------</span></p>
<p><st1:place w:st="on"><st1:state w:st="on"><span>DEL</span></st1:state></st1:place><span> runner.cpp</span></p>
<p>&nbsp;</p>
<p><span>放在项目&#8220;生成后事件&#8221;中的这一段命令，运行生成的测试，相当于</span><span>samples/msvc </span><span>第三个项目的功能。（即</span><span>Run</span><span>）</span></p>
<p>&nbsp;</p>
<p><span><span>5、&nbsp;</span></span><span>在<span>Unit_Test</span>&#8220;头文件&#8221;中，添加新的<span>.h</span>测试文件。</span></p>
<p><span>生成<span>Unit_Test</span>，即可在输出窗口分割线下看到运行测试的结果，双击结果行可以定位到源代码。</span></p>
<p><span>如：</span></p>
<p align=left><span>1&gt;-------------------- Running Unit Test Cases --------------------</span></p>
<p align=left><span>1&gt;Running 1 test</span></p>
<p align=left><span>1&gt;In MathsTestSuite::testMultiplication:</span></p>
<p align=left><span>1&gt;f:\test\cxxunittest\cxxunittest\sampletest.h(9): Error: Expected (2 * 2 == 5), found (4 != 5)</span></p>
<p align=left><span>1&gt;Failed 1 of 1 test</span></p>
<p align=left><span>1&gt;Success rate: 0%</span></p>
<p><span>1&gt;----------------------------------------------------------------------------------</span></p>
<p>&nbsp;</p>
<p><span>方法三：抽取出一个<span>Unit_Test</span>文件夹</span></p>
<p><span>所谓方法三，不过是将方法二中的<span>Unit_Test</span>文件夹，替代方法一中的<span>msvc</span>文件夹，移动到其它的项目中使用而已。</span></p>
<p><span>不像方法一的<span>msvc</span>那样需要三个项目，也不需像方法二那样，每次都修改<span>dummy</span>，和项目属性，添加命令行。将<span>Unit_Test</span>文件夹各个属性都配置好之后，独立出来随时备用。</span></p>
<p>&nbsp;</p>
<p><span>准备工作，如前所言。</span></p>
<p><span>要进行测试时：</span></p>
<p><span>将</span><span>Unit_test</span><span>文件夹拷贝放到工程目录中，在工程中添加里面的</span><span>Unit_Test</span><span>项目，仿照</span><span>Sampletest.h</span><span>写自己的测试文件。</span></p>
<p><span>运行测试时：</span></p>
<span>先编译</span><span>dummy</span><span>，再生成该测试项目。</span><span>(</span><span>如果没有另外添加</span><span>*.h</span><span>文件，直接生成就可以了</span><span>) </span>
<img src ="http://www.cppblog.com/shuiyuan2004/aggbug/31322.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shuiyuan2004/" target="_blank">不倦</a> 2007-08-31 18:42 <a href="http://www.cppblog.com/shuiyuan2004/archive/2007/08/31/31322.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++单元测试框架的比较[me]</title><link>http://www.cppblog.com/shuiyuan2004/archive/2007/08/29/31105.html</link><dc:creator>不倦</dc:creator><author>不倦</author><pubDate>Wed, 29 Aug 2007 01:30:00 GMT</pubDate><guid>http://www.cppblog.com/shuiyuan2004/archive/2007/08/29/31105.html</guid><wfw:comment>http://www.cppblog.com/shuiyuan2004/comments/31105.html</wfw:comment><comments>http://www.cppblog.com/shuiyuan2004/archive/2007/08/29/31105.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/shuiyuan2004/comments/commentRss/31105.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shuiyuan2004/services/trackbacks/31105.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>C++</span><span>单元测试框架的比较</span></p>
<p><span>单元测试现在已经成为标准的编程实践，但是</span><span>C++</span><span>缺少</span><span>Java</span><span>和</span><span>.Net</span><span>平台语言的反射机制，所以无法枚举测试方法，必须手工添加，或者使用一些特别的宏，弄得代码非常难看。</span><span>Java</span><span>语言单元测试是</span><span>JUnit</span><span>的天下，</span><span>C#</span><span>基本上都用</span><span>NUnit</span><span>，而</span><span>C++</span><span>则群花怒放，单元测试框架非常多，</span><span>JUnit</span><span>移植过来的</span><span>CppUnit</span><span>，</span><span>Boost::test</span><span>，</span><span>CppTest</span><span>，</span><span>CxxTest</span><span>，</span><span> TUT</span><span>等等。但是解决方案最好的是</span><span>CxxTest</span><span>和</span><span>TUT</span><span>，</span><span>CxxTest</span><span>采用的方法比较特殊，用</span><span>Perl</span><span>分析</span><span>C++</span><span>的源文件，从中抽取测试方法，创建</span><span>TestSuite</span><span>。语法与</span><span>JUnit</span><span>非常相似，没有使用高级的</span><span>C++</span><span>特性，也没有定义特别的宏，无须写额外的代码。</span><span>TUT</span><span>也是一个不错的解决方案，利用高级</span><span>C++ Template</span><span>功能，必须比较新的编译器才支持，比如</span><span>VC6</span><span>和</span><span>VS.NET 2002</span><span>就不支持，必须</span><span>VS.NET 2003</span><span>以上或者</span><span>Intel C++ Complier 8.1</span><span>以上。</span></p>
<p>&nbsp;</p>
<p><span><span>1、&nbsp;</span></span><span>TUT</span></p>
<p><span>结构框架简单。添加新的测试工作量小；无须注册测试；可移植性好（因其只需两个头文件，就可以完成测试工作）；便于装卸；提供接口可以扩展其输出方式等。</span></p>
<p><span>最大的优点：轻量级，便于装卸和可扩展其输出方式；</span></p>
<p><span>缺点：断言似乎不是很好，只用了一个</span><span>ensure()</span><span>函数，不知道对复杂的测试是否支持；输出的测试结果较为简单。</span></p>
<p><span><span>2、&nbsp;</span></span><span>Boost::test</span></p>
<p><span>结构框架较为复杂。添加新的测试工作量也不大；提供多种测试方法，可注册测试用例，也可不注册；可移植性一般；装卸不易；在控制异常、崩溃方面的能力胜过其它所有对手；拥有良好的断言功能；大概能支持多种输出方式，但更改输出方式不易；支持测试套件。</span></p>
<p><span>最大的优点：控制异常崩溃的能力、良好的断言、输出结果较为详细、编写测试的方法灵活；</span></p>
<p><span>缺点：结构框架较为复杂，更改输出方式不易，装卸不易。</span></p>
<p><span><span>3、&nbsp;</span></span><span>CXXTest</span></p>
<p><span>结构框架的复杂性处于</span><span>TUT</span><span>与</span><span>boost::test</span><span>之间。添加新的测试工作量非常小；无须注册测试用例；可移植性很好；便于装卸；控制异常、崩溃方面的能力也不错；拥有良好的断言功能；支持多种输出方式；支持测试套件。</span></p>
<p><span>最大的优点：</span><span>编译即测试方式，并且可以双击结果行立即定位到相应的源代码，相当吸引人；支持多种输出，输出结果较为详细；编写测试简单；</span></p>
<p><span>缺点：需要用到</span><span>perl</span><span>对测试代码进行文法扫描，生成可执行代码，需要用到</span><span>makefile</span><span>文件（不是必须）；准备工作比较麻烦。</span></p>
<img src ="http://www.cppblog.com/shuiyuan2004/aggbug/31105.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shuiyuan2004/" target="_blank">不倦</a> 2007-08-29 09:30 <a href="http://www.cppblog.com/shuiyuan2004/archive/2007/08/29/31105.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++测试框架的选择[转]</title><link>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30852.html</link><dc:creator>不倦</dc:creator><author>不倦</author><pubDate>Sun, 26 Aug 2007 06:34:00 GMT</pubDate><guid>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30852.html</guid><wfw:comment>http://www.cppblog.com/shuiyuan2004/comments/30852.html</wfw:comment><comments>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30852.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/shuiyuan2004/comments/commentRss/30852.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shuiyuan2004/services/trackbacks/30852.html</trackback:ping><description><![CDATA[<div class=cnt>
<p><font color=#800080><a href="http://www.gamesfromwithin.com/articles/0412/000061.html" target=_blank><u><font color=#0000ff>http://www.gamesfromwithin.com/articles/0412/000061.html</font></u></a>，</font>在这篇文章中，Noel Llopis提出了一个对C++ test framework评判的一些依据，按照Noel Llopis给出的重要性，我节略在这里。</p>
<p>1.加入新测试最小化工作量</p>
<p>2.便于修改和移植（作者的意思是说比如RTTI，STL，Exception这些高级特性可能妨碍在不同的平台，不同版本编译器下面的可移植性）</p>
<p>3.便于装配/拆卸测试环境</p>
<p>4.对异常以及崩溃很好的控制</p>
<p>5.好的断言功能</p>
<p>6.支持不同的输出方式</p>
<p>7.支持测试套件(suites)</p>
<p>&nbsp;</p>
<p>按照这个标准，Noel Llopis对下面的test framework进行了评价</p>
<p>CPPUnit</p>
<p>1.工作量多</p>
<p>2.CPPUnit能在Windows , Linux上面运行，功能进行了很好的模块化，但是另一方面，CPPUnit需要RTTI，STL，或者异常（作者不是很肯定）</p>
<p>3.</p>
<p>4.CPPUnit使用protectors包装测试，并且捕捉所有的异常（尝试识别某些异常），Linux下面不会捕捉系统异常，但是要增加自定义的包装是很容易的。</p>
<p>5.很好，支持一个最小集合的断言语句，包括比较浮点数。</p>
<p>6.支持</p>
<p>7.支持</p>
<p>总体评价：Overall, CppUnit is frustrating because it's almost exactly what I want, except for my most wanted feature. (CPPUnit够闷的，不过我觉得改进易用性应该可以期待）</p>
<p>&nbsp;</p>
<p>Boost.Test（我尝试使用，在VC.Net 2003下面遇到链接问题，还没有解决）</p>
<p>1.基本满足</p>
<p>2.和CPPUnit类似，但强调的是改代码的难度以及依赖Boost本身</p>
<p>3.避开了常规的setup/teardown结构，可以不需要动态生成fixture 对象，可以将fixture对象放到stack里面。</p>
<p>4.Boost.Test在这方面超过了所有的其他竞争对手</p>
<p>5.Yes</p>
<p>6.大概能支持，但改变输出这件事情并不是很容易</p>
<p>7.支持，...（这句如何理解？Yes, but with a big catch）</p>
<p>Overall,Boost.Test is a library with a huge amount of potential. It has great support for exception handling and advanced assert statements. It also has other fairly unique functionality such as support for checking for infinite loops, and different levels of logging. On the other hand, it's very verbose to add new tests that are part of a suite, and it might be a bit heavy weight for game console environments.</p>
<p>&nbsp;</p>
<p>CppUnitLite(由于作者比较了一个被他改动的版本，我不再关注）</p>
<p>&nbsp;</p>
<p>NanoCppUnit（这个库甚至需要你去从web pages上面copy代码，然后自己搞一个工程，我觉得我不太喜欢这种方式的package发布，毕竟，我希望少操心，所以我也不关注）</p>
<p>&nbsp;</p>
<p>Unit++</p>
<p>首先指出一个独特的特性：More C++ Like，作者的意思是它没有使用宏，的确，前面几种framework开始一个测试的时候都使用了宏，这在许多C++ Library中是惯例，用来简化一些代码。我们通过从基类继承从而创建测试包，当然在其他framework里面本质也是这样，但是都放在幕后进行，宏掩盖了具体情况。</p>
<p>1.不好</p>
<p>2.一般般</p>
<p>3.不支持</p>
<p>4.表现平均</p>
<p>5.文档没说如何支持不同的输出</p>
<p>6.不支持浮点数</p>
<p>7.支持</p>
<p>&nbsp;</p>
<p>CxxTest</p>
<p>首先作者认为文档最好（很重要？）另外作者指出,CxxTest的作者Erez Volk意识到我们是在写工具帮助测试C++程序，所以不必受限于C++的特征。 </p>
<p>1.非常好</p>
<p>2.很好</p>
<p>3.支持</p>
<p>4.很好</p>
<p>5.yes</p>
<p>6.yes</p>
<p>7.yes</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>文章最后给出一个综述：是个表现好的，CPPUnit, CppUnitLite, Boost.Test, CxxTest，作者本人喜欢CxxTest.</p>
<p>(完) </p>
<p>&nbsp;</p>
</div>
转自：<a href="http://hi.baidu.com/fangfang%5Fi/blog/item/32876bfb5d140a64024f56e6.html"><u><font color=#0000ff>http://hi.baidu.com/fangfang%5Fi/blog/item/32876bfb5d140a64024f56e6.html</font></u></a> 
<img src ="http://www.cppblog.com/shuiyuan2004/aggbug/30852.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shuiyuan2004/" target="_blank">不倦</a> 2007-08-26 14:34 <a href="http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30852.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>全面介绍单元测试 －转贴</title><link>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30851.html</link><dc:creator>不倦</dc:creator><author>不倦</author><pubDate>Sun, 26 Aug 2007 06:33:00 GMT</pubDate><guid>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30851.html</guid><wfw:comment>http://www.cppblog.com/shuiyuan2004/comments/30851.html</wfw:comment><comments>http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30851.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/shuiyuan2004/comments/commentRss/30851.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/shuiyuan2004/services/trackbacks/30851.html</trackback:ping><description><![CDATA[<span class=smalltxt><span class=bold><font size=2>
<p><font size=2><span class=smalltxt><span class=bold>全面介绍单元测试 －转贴</span></span><br><br></font><span style="FONT-SIZE: 12px"><font size=2>这是一篇全面介绍单元测试的经典之作，对理解单元测试和Visual Unit很有帮助，作者老纳，收录时作了少量修改<br><br>一　单元测试概述<br>　　工厂在组装一台电视机之前，会对每个元件都进行测试，这，就是单元测试。<br>　　其实我们每天都在做单元测试。你写了一个函数，除了极简单的外，总是要执行一下，看看功能是否正常，有时还要想办法输出些数据，如弹出信息窗口什么的，这，也是单元测试，老纳把这种单元测试称为临时单元测试。只进行了临时单元测试的软件，针对代码的测试很不完整，代码覆盖率要超过70%都很困难，未覆盖的代码可能遗留大量的细小的错误，这些错误还会互相影响，当BUG暴露出来的时候难于调试，大幅度提高后期测试和维护成本，也降低了开发商的竞争力。可以说，进行充分的单元测试，是提高软件质量，降低开发成本的必由之路。<br>　　对于程序员来说，如果养成了对自己写的代码进行单元测试的习惯，不但可以写出高质量的代码，而且还能提高编程水平。<br>　　要进行充分的单元测试，应专门编写测试代码，并与产品代码隔离。老纳认为，比较简单的办法是为产品工程建立对应的测试工程，为每个类建立对应的测试类，为每个函数（很简单的除外）建立测试函数。首先就几个概念谈谈老纳的看法。<br>　　一般认为，在结构化程序时代，单元测试所说的单元是指函数，在当今的面向对象时代，单元测试所说的单元是指类。以老纳的实践来看，以类作为测试单位，复杂度高，可操作性较差，因此仍然主张以函数作为单元测试的测试单位，但可以用一个测试类来组织某个类的所有测试函数。单元测试不应过分强调面向对象，因为局部代码依然是结构化的。单元测试的工作量较大，简单实用高效才是硬道理。<br>　　有一种看法是，只测试类的接口(公有函数)，不测试其他函数，从面向对象角度来看，确实有其道理，但是，测试的目的是找错并最终排错，因此，只要是包含错误的可能性较大的函数都要测试，跟函数是否私有没有关系。对于C++来说，可以用一种简单的方法区隔需测试的函数：简单的函数如数据读写函数的实现在头文件中编写(inline函数)，所有在源文件编写实现的函数都要进行测试(构造函数和析构函数除外)。<br>　　什么时候测试？单元测试越早越好，早到什么程度？XP开发理论讲究TDD，即测试驱动开发，先编写测试代码，再进行开发。在实际的工作中，可以不必过分强调先什么后什么，重要的是高效和感觉舒适。从老纳的经验来看，先编写产品函数的框架，然后编写测试函数，针对产品函数的功能编写测试用例，然后编写产品函数的代码，每写一个功能点都运行测试，随时补充测试用例。所谓先编写产品函数的框架，是指先编写函数空的实现，有返回值的随便返回一个值，编译通过后再编写测试代码，这时，函数名、参数表、返回类型都应该确定下来了，所编写的测试代码以后需修改的可能性比较小。<br>　　由谁测试？单元测试与其他测试不同，单元测试可看作是编码工作的一部分，应该由程序员完成，也就是说，经过了单元测试的代码才是已完成的代码，提交产品代码时也要同时提交测试代码。测试部门可以作一定程度的审核。<br>　　关于桩代码，老纳认为，单元测试应避免编写桩代码。桩代码就是用来代替某些代码的代码，例如，产品函数或测试函数调用了一个未编写的函数，可以编写桩函数来代替该被调用的函数，桩代码也用于实现测试隔离。采用由底向上的方式进行开发，底层的代码先开发并先测试，可以避免编写桩代码，这样做的好处有：减少了工作量；测试上层函数时，也是对下层函数的间接测试；当下层函数修改时，通过回归测试可以确认修改是否导致上层函数产生错误。<br><br>二　测试代码编写<br>　　多数讲述单元测试的文章都是以Java为例，本文以C++为例，后半部分所介绍的单元测试工具也只介绍C++单元测试工具。下面的示例代码的开发环境是VC6.0。<br><br>产品类：<br>class CMyClass <br>{<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int Add(int i, int j);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CMyClass();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual ~CMyClass();<br><br>private:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int mAge;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //年龄<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CString mPhase; //年龄阶段，如"少年"，"青年"<br>};<br><br>建立对应的测试类CMyClassTester，为了节约编幅，只列出源文件的代码：<br>void CMyClassTester::CaseBegin()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //pObj是CMyClassTester类的成员变量，是被测试类的对象的指针，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //为求简单，所有的测试类都可以用pObj命名被测试对象的指针。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pObj = new CMyClass();<br>}<br><br>void CMyClassTester::CaseEnd()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delete pObj;<br>}<br>测试类的函数CaseBegin()和CaseEnd()建立和销毁被测试对象，每个测试用例的开头都要调用CaseBegin()，结尾都要调用CaseEnd()。<br><br>接下来，我们建立示例的产品函数：<br>int CMyClass::Add(int i, int j)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return i+j;<br>}<br>和对应的测试函数：<br>void CMyClassTester::Add_int_int()<br>{<br>}<br>把参数表作为函数名的一部分，这样当出现重载的被测试函数时，测试函数不会产生命名冲突。下面添加测试用例：<br>void CMyClassTester::Add_int_int()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //第一个测试用例<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CaseBegin();{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int j = 0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //3<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int ret = pObj-&gt;Add(i, j); //4<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(ret == 0);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //5<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }CaseEnd();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //6<br>}<br>第1和第6行建立和销毁被测试对象，所加的{}是为了让每个测试用例的代码有一个独立的域，以便多个测试用例使用相同的变量名。<br>第2和第3行是定义输入数据，第4行是调用被测试函数，这些容易理解，不作进一步解释。第5行是预期输出，它的特点是当实际输出与预期输出不同时自动报错，ASSERT是VC的断言宏，也可以使用其他类似功能的宏，使用测试工具进行单元测试时，可以使用该工具定义的断言宏。<br><br>　　示例中的格式显得很不简洁，2、３、4、5行可以合写为一行：ASSERT(pObj-&gt;Add(0, 0) == 0);但这种不简洁的格式却是老纳极力推荐的，因为它一目了然，易于建立多个测试用例，并且具有很好的适应性，同时，也是极佳的代码文档，总之，老纳建议：输入数据和预期输出要自成一块。<br>　　建立了第一个测试用例后，应编译并运行测试，以排除语法错误，然后，使用拷贝/修改的办法建立其他测试用例。由于各个测试用例之间的差别往往很小，通常只需修改一两个数据，拷贝/修改是建立多个测试用例的最快捷办法。<br><br>三　测试用例<br>　　下面说说测试用例、输入数据及预期输出。输入数据是测试用例的核心，老纳对输入数据的定义是：被测试函数所读取的外部数据及这些数据的初始值。外部数据是对于被测试函数来说的，实际上就是除了局部变量以外的其他数据，老纳把这些数据分为几类：参数、成员变量、全局变量、IO媒体。IO媒体是指文件、数据库或其他储存或传输数据的媒体，例如，被测试函数要从文件或数据库读取数据，那么，文件或数据库中的原始数据也属于输入数据。一个函数无论多复杂，都无非是对这几类数据的读取、计算和写入。预期输出是指：返回值及被测试函数所写入的外部数据的结果值。返回值就不用说了，被测试函数进行了写操作的参数(输出参数)、成员变量、全局变量、IO媒体，它们的预期的结果值都是预期输出。一个测试用例，就是设定输入数据，运行被测试函数，然后判断实际输出是否符合预期。下面举一个与成员变量有关的例子：<br>产品函数：<br>void CMyClass::Grow(int years)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mAge += years;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(mAge &lt; 10)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mPhase = "儿童";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(mAge &lt;20)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mPhase = "少年";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(mAge &lt;45)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mPhase = "青年";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(mAge &lt;60)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mPhase = "中年";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mPhase = "老年";<br>}<br><br>测试函数中的一个测试用例：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CaseBegin();{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int years = 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pObj-&gt;mAge = 8;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pObj-&gt;Grow(years);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT( pObj-&gt;mAge == 9 );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT( pObj-&gt;mPhase == "儿童" );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }CaseEnd();<br>在输入数据中对被测试类的成员变量mAge进行赋值，在预期输出中断言成员变量的值。现在可以看到老纳所推荐的格式的好处了吧，这种格式可以适应很复杂的测试。在输入数据部分还可以调用其他成员函数，例如：执行被测试函数前可能需要读取文件中的数据保存到成员变量，或需要连接数据库，老纳把这些操作称为初始化操作。例如，上例中 ASSERT( ...)之前可以加pObj-&gt;OpenFile();。为了访问私有成员，可以将测试类定义为产品类的友元类。例如，定义一个宏：<br>#define UNIT_TEST(cls) friend class cls##Tester;<br>然后在产品类声明中加一行代码：UNIT_TEST(ClassName)。<br><br>　　下面谈谈测试用例设计。前面已经说了，测试用例的核心是输入数据。预期输出是依据输入数据和程序功能来确定的，也就是说，对于某一程序，输入数据确定了，预期输出也就可以确定了，至于生成/销毁被测试对象和运行测试的语句，是所有测试用例都大同小异的，因此，我们讨论测试用例时，只讨论输入数据。<br>　　前面说过，输入数据包括四类：参数、成员变量、全局变量、IO媒体，这四类数据中，只要所测试的程序需要执行读操作的，就要设定其初始值，其中，前两类比较常用，后两类较少用。显然，把输入数据的所有可能取值都进行测试，是不可能也是无意义的，我们应该用一定的规则选择有代表性的数据作为输入数据，主要有三种：正常输入，边界输入，非法输入，每种输入还可以分类，也就是平常说的等价类法，每类取一个数据作为输入数据，如果测试通过，可以肯定同类的其他输入也是可以通过的。下面举例说明： <br>　　正常输入<br>　　例如字符串的Trim函数，功能是将字符串前后的空格去除，那么正常的输入可以有四类：前面有空格；后面有空格；前后均有空格；前后均无空格。<br>　　边界输入<br>　　上例中空字符串可以看作是边界输入。<br>　　再如一个表示年龄的参数，它的有效范围是0-100，那么边界输入有两个：0和100。<br>　　非法输入<br>　　非法输入是正常取值范围以外的数据，或使代码不能完成正常功能的输入，如上例中表示年龄的参数，小于0或大于100都是非法输入，再如一个进行文件操作的函数，非法输入有这么几类：文件不存在；目录不存在；文件正在被其他程序打开；权限错误。<br>　　如果函数使用了外部数据，则正常输入是肯定会有的，而边界输入和非法输入不是所有函数都有。一般情况下，即使没有设计文档，考虑以上三种输入也可以找出函数的基本功能点。实际上，单元测试与代码编写是&#8220;一体两面&#8221;的关系，编码时对上述三种输入都是必须考虑的，否则代码的健壮性就会成问题。<br><br>四　白盒覆盖<br>　　上面所说的测试数据都是针对程序的功能来设计的，就是所谓的黑盒测试。单元测试还需要从另一个角度来设计测试数据，即针对程序的逻辑结构来设计测试用例，就是所谓的白盒测试。在老纳看来，如果黑盒测试是足够充分的，那么白盒测试就没有必要，可惜&#8220;足够充分&#8221;只是一种理想状态，例如：真的是所有功能点都测试了吗？程序的功能点是人为的定义，常常是不全面的；各个输入数据之间，有些组合可能会产生问题，怎样保证这些组合都经过了测试？难于衡量测试的完整性是黑盒测试的主要缺陷，而白盒测试恰恰具有易于衡量测试完整性的优点，两者之间具有极好的互补性，例如：完成功能测试后统计语句覆盖率，如果语句覆盖未完成，很可能是未覆盖的语句所对应的功能点未测试。<br>　　白盒测试针对程序的逻辑结构设计测试用例，用逻辑覆盖率来衡量测试的完整性。逻辑单位主要有：语句、分支、条件、条件值、条件值组合，路径。语句覆盖就是覆盖所有的语句，其他类推。另外还有一种判定条件覆盖，其实是分支覆盖与条件覆盖的组合，在此不作讨论。跟条件有关的覆盖就有三种，解释一下：条件覆盖是指覆盖所有的条件表达式，即所有的条件表达式都至少计算一次，不考虑计算结果；条件值覆盖是指覆盖条件的所有可能取值，即每个条件的取真值和取假值都要至少计算一次；条件值组合覆盖是指覆盖所有条件取值的所有可能组合。老纳做过一些粗浅的研究，发现与条件直接有关的错误主要是逻辑操作符错误，例如：||写成&amp;&amp;，漏了写!什么的，采用分支覆盖与条件覆盖的组合，基本上可以发现这些错误，另一方面，条件值覆盖与条件值组合覆盖往往需要大量的测试用例，因此，在老纳看来，条件值覆盖和条件值组合覆盖的效费比偏低。老纳认为效费比较高且完整性也足够的测试要求是这样的：完成功能测试，完成语句覆盖、条件覆盖、分支覆盖、路径覆盖。做过单元测试的朋友恐怕会对老纳提出的测试要求给予一个字的评价：晕！或者两个字的评价：狂晕！因为这似乎是不可能的要求，要达到这种测试完整性，其测试成本是不可想象的，不过，出家人不打逛语，老纳之所以提出这种测试要求，是因为利用一些工具，可以在较低的成本下达到这种测试要求，后面将会作进一步介绍。<br>　　关于白盒测试用例的设计，程序测试领域的书籍一般都有讲述，普通方法是画出程序的逻辑结构图如程序流程图或控制流图，根据逻辑结构图设计测试用例，这些是纯粹的白盒测试，不是老纳想推荐的方式。老纳所推荐的方法是：先完成黑盒测试，然后统计白盒覆盖率，针对未覆盖的逻辑单位设计测试用例覆盖它，例如，先检查是否有语句未覆盖，有的话设计测试用例覆盖它，然后用同样方法完成条件覆盖、分支覆盖和路径覆盖，这样的话，既检验了黑盒测试的完整性，又避免了重复的工作，用较少的时间成本达到非常高的测试完整性。不过，这些工作可不是手工能完成的，必须借助于工具，后面会介绍可以完成这些工作的测试工具。<br><br>五　单元测试工具<br>　　现在开始介绍单元测试工具，老纳只介绍三种，都是用于C++语言的。<br>　　首先是CppUnit，这是C++单元测试工具的鼻祖，免费的开源的单元测试框架。由于已有一众高人写了不少关于CppUnit的很好的文章，老纳就不现丑了，想了解CppUnit的朋友，建议读一下Cpluser 所作的《CppUnit测试框架入门》，网址是：</font><a href="http://blog.csdn.net/cpluser/archive/2004/09/21/111522.aspx" target=_blank><font color=#003366 size=2><u>http://blog.csdn.net/cpluser/archive/2004/09/21/111522.aspx</u></font></a><font size=2>。该文也提供了CppUnit的下载地址。<br>　　然后介绍C++Test，这是Parasoft公司的产品。［C++Test是一个功能强大的自动化C/C++单元级测试工具，可以自动测试任何C/C++函数、类，自动生成测试用例、测试驱动函数或桩函数，在自动化的环境下极其容易快速的将单元级的测试覆盖率达到100%。</font><font size=2>这是华唐公司的网页上的介绍。老纳想写些介绍C++Test的文字，但发现无法超越华唐公司的网页上的介绍，所以也就省点事了，想了解C++Test的朋友，建议访问该公司的网站。华唐公司代理C++Test，想要购买或索取报价、试用版都可以找他们。老纳帮华唐公司做广告，不知道会不会得点什么好处？<br>　　最后介绍Visual Unit，简称VU，这是国产的单元测试工具，据说申请了多项专利，拥有一批创新的技术，不过老纳只关心是不是有用和好用。［自动生成测试代码 快速建立功能测试用例 程序行为一目了然 极高的测试完整性 高效完成白盒覆盖 快速排错 高效调试 详尽的测试报告］。［］内的文字是VU开发商的网页上摘录的，网址是：</font><a href="http://www.unitware.cn/" target=_blank><font color=#003366 size=2><u>http://www.unitware.cn</u></font></a><font size=2>。前面所述测试要求：完成功能测试，完成语句覆盖、条件覆盖、分支覆盖、路径覆盖，用VU可以轻松实现，还有一点值得一提：使用VU还能提高编码的效率，总体来说，在完成单元测试的同时，编码调试的时间还能大幅度缩短。算了，不想再讲了，老纳显摆理论、介绍经验还是有兴趣的，因为可以满足老纳好为人师的虚荣心，但介绍工具就觉得索然无味了，毕竟工具好不好用，合不合用，要试过才知道，还是自己去开发商的网站看吧，可以下载演示版，还有演示课件。</font></span></p>
<p><span style="FONT-SIZE: 12px"><font size=2>转自：</font><a href="http://www.cnblogs.com/tester2test/archive/2006/08/04/467764.html"><font color=#0000ff size=2><u>http://www.cnblogs.com/tester2test/archive/2006/08/04/467764.html</u></font></a></span></p>
</font></span></span>
<img src ="http://www.cppblog.com/shuiyuan2004/aggbug/30851.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/shuiyuan2004/" target="_blank">不倦</a> 2007-08-26 14:33 <a href="http://www.cppblog.com/shuiyuan2004/archive/2007/08/26/30851.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>