<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>白衬衫，蓝色忧伤...</title>
    <description>信心＋勇气
寻找圣杯！</description>
    <link>http://hyysguyang.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>这样的TDD实践方式有问题？请教大家的TDD实施方式.</title>
        <author>hyysguyang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hyysguyang.javaeye.com">hyysguyang</a>&nbsp;
          链接：<a href="http://hyysguyang.javaeye.com/blog/122472" style="color:red;">http://hyysguyang.javaeye.com/blog/122472</a>&nbsp;
          发表时间: 2007年09月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          我一直都这么做，可是在一次实践交流(其实就是面试，一家号称采用敏捷开发的公司)之中，我这样的方式被称之为钻了牛角尖。”你是不是从书上随便看了一下TDD相关的资料之后，就认为你已经掌握了TDD了？”，我一直都这么做，目前已经有几个项目都是采用这样的开发方式，可是，在这次的交流之后我可是有点动摇了，我是不是做错了?我想知道大家都怎么样运用TDD的？而我是这样做的，就看看那次交流现场开发的吧：<br />以下称那个朋友为TDDer（可是两个人哦，连面试都是PP，）吧。<br />TDDer提的需求:把字符串"Tdd is a software devolopment technology"  按照单词反转为<br />"technology devolopment software a is Tdd"<br />在开始之前，我问了两次TDDer，确认需求是不是这样，确认需求如此之后，我就开始了：<br />1.	编写测试代码:<br /><pre name="code" class="java">public class StringReverseTest {

    @Test
    public void testReverse(){
        StringReverser sr=new StringReverser();
        String str = "Tdd is a software devolopment technology";
        Assert.assertEquals("technology devolopment software a is Tdd",sr.reverse(str));
    }

}</pre><br /><br />2.	编写产品代码<br /><pre name="code" class="java">public class StringReverser {
    public String reverse(String str) {
        return "technology devolopment software a is Tdd";
    }
}</pre><br />	然后完成.<br />之后给TDDer说完成了，然后，TDDer问我，这样就完成了?<br />我说功能是实现了，可以说完成了，不过还有一步，重构，去除重复代码.TDDer仅仅看了一眼就直摇头,他说“你写这样的代码有什么用，如果我换另外一个字符串呢?你这个还能跑么？"，我说"你的需求只是转那个字符串，而且我找你确认过，如果你要换另外一个字符串，那也就是说需求变更，那我调整代码罗,我说TDD不是这样的么？我平时工作中就这么做的。"，TDDer只有一个劲的摇头了，而我也知道，这次没戏了.:D <br />其实，我知道我还有一步没做，那就是重构，我当时在白纸上写的，嘿嘿，根本没法运行测试，所以这一步也比较麻烦，尽管重构可以不依赖于测试，但我已经习惯于依赖测试。<br />谈论了一些话题之后：我收到的临别赠言是：<br />“钻了牛角尖。”<br />”你是不是从书上随便看了一下TDD相关的资料之后，就认为你已经掌握了TDD了？”<br />“Code Smell”<br />“你这个是十足的TDD的反模式,如果你这个给我们公司的xxxx看的话，这个一定是他培训时举的最好的反模式了”<br />	于是，我向他们咨询他们是怎么实施TDD的?可是我最后还是没有答案.	<br />终于，讨论结束了，我带着满腔困惑和不解回到家，打开笔记本，开始了这个征程：<br />1. 建立eclipse工程，然后编写测试代码，并运行测试，失败了。<br />测试代码：	<br /><pre name="code" class="java"> package org.opensource.test;
import org.junit.Assert;
import org.junit.Test;
public class StringReverseTest {
    @Test
    public void testReverse(){
        StringReverser sr=new StringReverser();
        String str = "Tdd is a software devolopment technology";
        Assert.assertEquals("technology devolopment software a is Tdd",sr.reverse(str));
    }
}
</pre><br /><br />产品代码：<br /><pre name="code" class="java">package org.opensource.test;
public class StringReverser {
    public Object reverse(String str) {
        return null;
    }
}</pre><br /><br />2. 编写产品代码，确保测试通过：<br /><pre name="code" class="java">
package org.opensource.test;
public class StringReverser {
    public Object reverse(String str) {
        return "technology devolopment software a is Tdd";
    }
}</pre><br /> <br />3. 重构，消除重复代码：<br /><pre name="code" class="java">package org.opensource.test;
public class StringReverser {
    private static final String SPACE = " ";
    public String reverse(String str) {
        String[] words=str.split(SPACE);
        StringBuilder result=new StringBuilder();
        for (int i = words.length-1; i >=0; i--) {
            result.append(words[i]).append(SPACE);
        }
        return result.toString().trim();
    }
} </pre><br /><br /><br /><br />完了，这样已经完了.<br /><br />后来和一个朋友聊到这个事情，他也笑的不成样子，他认为我很懒，应该写完整点。而我始终认为我这样做是对的，而且现实中我也是这么做。我跟朋友说，如果要说有问题，那就是我本应该引导客户，他的需求的确是这个么？可是，当时TDDer要要看看我写的代码的风格，而不是于客户的沟通.当然我这个朋友并不是一个TDD的爱好者.而我是一个TDD迷恋者<br /><br />我不知道我这样做是不是不对,因此到这里来请教大家，你们都是怎么做的?<br />我承认，对于TDD的理论，我的确是从书上看得，从网上学到，但是跟多的是从开源里面去体会，在项目产品中去实践。我已经在四个项目中采用这样的方式，一直都感觉很好.<br />因此，很是困惑，请大家指点指点，我想知道大家都是怎么实施TDD的.<br /><br />	在我的开发生活中，TDD,Refactor.CI是三个最重要也最常见的伙伴,我就是这样实践他们的.<br /><br />虽然这个demo很简单，但是足以说明我的开发工程.<br />当然这里写起来这么麻烦，但是在开发这样的一个demo过程中其实是很快的。<br /><br />这次的经历告诉我：在面试时，要看看一个人的TDD能力，一定要在电脑上做，真真实实的去做，这样才知道，事实上，当时我带了笔记本去了.<br /><br />我永远不会忘记：Clean code that works.<br /><br />PS:<a href="http://www.javaeye.com/topic/94867?page=1" target="_blank">把数据库扔在一边！(请直接下载我最后回复中的详细文档和源代码吧) </a>曾引起强劲的反映，我现在在做的一个项目中，目前功能基本实现，现在正在处理扣费部分，到目前为止，都未考虑数据库，数据当前都是序列化到文件系统中的。:D 我只是想说的是，不说其他的，如果考虑数据库，单单我运行测试的时间都要浪费不少，效率都要低很多，只从这点上考虑，我已经非常不愿意在前期考虑数据库的设计了.在我看来，数据的存储方式本就不应该影响业务模型及业务逻辑.<br /><br /><br />附件是这个demo的源码.<img src="/images/smiles/icon_biggrin.gif"/>
          <br/>
          <span style="color:red;">
            <a href="http://hyysguyang.javaeye.com/blog/122472#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 10 Sep 2007 01:39:36 +0800</pubDate>
        <link>http://hyysguyang.javaeye.com/blog/122472</link>
        <guid>http://hyysguyang.javaeye.com/blog/122472</guid>
      </item>
      <item>
        <title>Struts1 资源配置重装载</title>
        <author>hyysguyang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hyysguyang.javaeye.com">hyysguyang</a>&nbsp;
          链接：<a href="http://hyysguyang.javaeye.com/blog/71573" style="color:red;">http://hyysguyang.javaeye.com/blog/71573</a>&nbsp;
          发表时间: 2007年04月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Struts1 的资源一直不能重装载，严重影响开发效率，经历了一个上午的折腾之后，决定解决这个问题，否则，效率太低了.<br />其实实现也很简单，当前基于filter，当然，如果需要移植到其他的实现方式也是易如反掌的。<br />目前仅支持struts1不支持struts2，我对struts2不熟悉，没什么兴趣，struts1也是公司采用才没有办法的，不过，如果要支持struts2，我想也不是什么难事。<br />当前支持的配置包括：struts-config，message resources资源文件以及Plugin的配置。对于Plugin的配置仅仅测试过验证器validate配置和tiles插件的配置，其他的未测试.<br /><br />配置如下：在web.xml中添加以下配置即可，建议产品上线时注释或者删除该配置<br /><pre name="code" class="java">&lt;!-- StrutsCofigReloadFilter  start -->
  &lt;filter>
    &lt;filter-name>strutsCofigReloadFilter&lt;/filter-name>
    &lt;filter-class>
         com.numen.framework.struts.filter.StrutsCofigReloadFilter
    &lt;/filter-class>
  &lt;/filter>

  &lt;filter-mapping>
    &lt;filter-name>strutsCofigReloadFilter&lt;/filter-name>
    &lt;url-pattern>*.do&lt;/url-pattern>
  &lt;/filter-mapping>

  &lt;!-- StrutsCofigReloadFilter  end --></pre><br /><br />附件是整个项目文件.<br />Enjoy youself!
          <br/>
          <span style="color:red;">
            <a href="http://hyysguyang.javaeye.com/blog/71573#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 16 Apr 2007 21:23:00 +0800</pubDate>
        <link>http://hyysguyang.javaeye.com/blog/71573</link>
        <guid>http://hyysguyang.javaeye.com/blog/71573</guid>
      </item>
      <item>
        <title>敏捷？敏捷?敏捷 ……</title>
        <author>hyysguyang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hyysguyang.javaeye.com">hyysguyang</a>&nbsp;
          链接：<a href="http://hyysguyang.javaeye.com/blog/68158" style="color:red;">http://hyysguyang.javaeye.com/blog/68158</a>&nbsp;
          发表时间: 2007年04月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">如果你根本就没有尝过某一卷冰淇淋的甜头，你就跟别人说这个很甜，我相信这个是很难让人信服的</span><span lang="EN-US">.</span><span style="font-family: 宋体;">只有他去尝过太才会确定是不是甜的，说不定他觉的很苦很涩。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">并不是书上说的都是对的。敏捷是一种思想。而实际上敏捷方法是一系列的实践，比如</span><span lang="EN-US">TDD,Refactoring,Continuous Integration</span><span style="font-family: 宋体;">等等，这些都是久经考验的技术实践，而且，这些技术很多时候都是一环扣一环的，你不会写测试，怎么</span><span lang="EN-US">TDD</span><span style="font-family: 宋体;">？你说你不</span><span lang="EN-US">TDD</span><span style="font-family: 宋体;">，我只写测试，可是你设计的系统更本就没法测试，写一个测试要花你很多很多时间，你还会继续么？重构，常常听到很多人在耳边想起，可是，他们却不知道重构的基础是有详尽的</span><span lang="EN-US">Test Suite</span><span style="font-family: 宋体;">，听他们这么说，我只能这样理解，他们根本就没看过</span><span lang="EN-US">Martin</span><span style="font-family: 宋体;">的名著重构，因为他们都从不写测试用例的。无法可想，实际我们现在已经有了很多很多的测试用例了，只可惜，这些用例都没有用，一但运行，红了一大片，而且很多测试都达不到目的，实际上我们当前项目有些</span><span lang="EN-US">member</span><span style="font-family: 宋体;">居然把测试用例当作炫耀的资本，&ldquo;看看，我们写了多少测试</span><span lang="EN-US">&hellip;</span><span style="font-family: 宋体;">&rdquo;，但他却没问他自己，&ldquo;你浪费了多少时间</span><span lang="EN-US">&hellip;.</span><span style="font-family: 宋体;">&rdquo;。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">没有用的测试用例远比没有测试用例还要糟糕，因为那不但浪费时间还浪费金钱</span><span lang="EN-US">&hellip;&hellip;</span><span style="font-family: 宋体;">如果你做某一件事情不会给你带来好处，我相信你绝对不愿去做，当然，如果可以给你带来炫耀的资本，我想你还是会去做的。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">不是每个人都像</span><span lang="EN-US">Kent Benk</span><span style="font-family: 宋体;">那样迷恋绿色条的，当然习惯</span><span lang="EN-US">TDD</span><span style="font-family: 宋体;">的也很快就恋上那绿色条的。中国的开发人员太虚太浮了（当然并不是所有），至少我现在感觉周围的很多人都是这样</span><span lang="EN-US">.</span><span style="font-family: 宋体;">两个方安放在一起，都没测过，就说这个方案性能没有那个方案好，你以前有过这方面的经验么？你测过么？都没有，那你简直就是在放屁，想当然的。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">不要想当然了，你不去做你永远都不知道的。</span><span lang="EN-US" style="font-size: 11pt;"><o:p></o:p></span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span lang="EN-US">OO</span><span style="font-family: 宋体;">技术已经这么多年了，可是又有多少人能合理很好也运用？做一样东西不去弄清楚就瞎掰瞎侃。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">其实最终还是人的问题，如果你跟一个一年级的小朋友说：</span><span lang="EN-US">1 x 100</span><span style="font-family: 宋体;">比</span><span lang="EN-US">100</span><span style="font-family: 宋体;">个</span><span lang="EN-US">1</span><span style="font-family: 宋体;">加起来要方便快捷，可是他认为</span><span lang="EN-US">100</span><span style="font-family: 宋体;">个</span><span lang="EN-US">1</span><span style="font-family: 宋体;">加起来要方便快捷，而且那个小朋友不信任你，因此他就是不认同你的观点，那你还能怎么办？我想只能无语了。敏捷开发里很重要的一点就是信任，你要信任你的搭挡，信任他们，很郁闷的是，我在一次和同事的聊天中提到，每人都应该信任项目组的所有人，我却没想到的是后来在一次开会中，居然说我认为他不信任</span><span lang="EN-US">&hellip;&hellip;.</span><span style="font-family: 宋体;">遇到这样的同事（应该算是</span><span lang="EN-US">TeamLeader</span><span style="font-family: 宋体;">吧）我无法可想，当时唯一的想法就是辞职。两个人之间都已经失去了信任还在一起做什么？即便在一起也做不出什么东西出来。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">事实上敏捷开发设计很多技术，如果你要敏捷，你需要具备很多技术底蕴，像</span><span lang="EN-US">OO</span><span style="font-family: 宋体;">，模式，</span><span lang="EN-US">TDD</span><span style="font-family: 宋体;">，重构，持续集成，还有更重要的思维方式等等</span><span lang="EN-US">,</span><span style="font-family: 宋体;">其要要敏捷不是一件容易的事，并不是你什么时候想敏捷就敏捷的。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">人，软件开发绝对是人。一个项目如果失败，多数情况下一定是人的问题。务实点，一个人务实点总是好的</span><span lang="EN-US">.</span><span style="font-family: 宋体;">。</span></p>
<p class="MsoNormal" style="text-indent: 21pt;"><span style="font-family: 宋体;">每个人都会犯错，只要犯错就要付出代价的</span><span lang="EN-US">.</span></p>
          <br/>
          <span style="color:red;">
            <a href="http://hyysguyang.javaeye.com/blog/68158#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 04 Apr 2007 21:12:54 +0800</pubDate>
        <link>http://hyysguyang.javaeye.com/blog/68158</link>
        <guid>http://hyysguyang.javaeye.com/blog/68158</guid>
      </item>
      <item>
        <title>C/C++利剑－Eclipse 的CDT-4.0.0-M5终于发布了</title>
        <author>hyysguyang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hyysguyang.javaeye.com">hyysguyang</a>&nbsp;
          链接：<a href="http://hyysguyang.javaeye.com/blog/60417" style="color:red;">http://hyysguyang.javaeye.com/blog/60417</a>&nbsp;
          发表时间: 2007年03月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 等了很久了，<font face="Arial">Eclipse 的CDT-4.0.0-M5终于发布了，虽然不是release，但是已经可以用了，可以慢慢的等他的release版本了.</font></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以前写写C或者C++总觉得没有很顺手的工具，用VC也不知道为什么，总是感觉不爽，估计是自己不太喜欢付费的东西吧，如果一个不用付费的和另一个付费的只是相差那么一点点，我想我还是宁愿使用免费的，更何况Eclipse本来就不比VC。以前用VS.net 2003，总是感觉不爽,也许是我不会用它吧，总之开发C或者C++ eclipse还是我的首选。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font face="Arial">遗憾的是，之前的CDT在代码辅助的时候速度奇慢，根本没法用，当然我说的是使用sun的JDK的时候,如果使用IBM的JDK的话，是没有任何问题的，<font face="Arial">而CDT-4.0.0已经没有这个问题，使用sun的jdk运行还是很好,真的很高兴.</font></font></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 实际上CDT-4.0.0在2007－2－20号就已经发布了，只可惜这段时间一直很忙，一直没到这个地方发泄.</p>
<p>终于发泄了一下心中的快感。</p>
<p><font face="Arial"><a href="http://www.eclipse.org/cdt/">www.eclipse.org/cdt/</a></font></p>
          <br/>
          <span style="color:red;">
            <a href="http://hyysguyang.javaeye.com/blog/60417#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 16 Mar 2007 23:10:00 +0800</pubDate>
        <link>http://hyysguyang.javaeye.com/blog/60417</link>
        <guid>http://hyysguyang.javaeye.com/blog/60417</guid>
      </item>
      <item>
        <title>持续集成实践之CruiseControl</title>
        <author>hyysguyang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hyysguyang.javaeye.com">hyysguyang</a>&nbsp;
          链接：<a href="http://hyysguyang.javaeye.com/blog/38610" style="color:red;">http://hyysguyang.javaeye.com/blog/38610</a>&nbsp;
          发表时间: 2006年12月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div align="center"><font size="4"><strong><span style="FONT-SIZE: 22pt">持续集成实践之</span></strong><span style="FONT-SIZE: 22pt">CruiseControl</span></font></div>
<div align="center">hyysguyang 2006-1024</div>
<div align="center">&nbsp;<span style="FONT-SIZE: 10.5pt"><br clear="all" />
</span></div>
<div style="MARGIN: 17pt 0cm 16.5pt"><strong><font size="3"><span>1. </span><span><span>疲于奔命得一天</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>这么模块以前已经编写好得，现在怎么出问题了？</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>上个礼拜编写得测试用例怎么现在通不过了？</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>终于完成了一个功能了，提交代码，突然间想起昨天花了很大精力修复的那个bug，那个模块可是以前就已经完成了的，心惊胆颤，我现在完成的这个模块是否对其他模块产生影响？<span>&nbsp;&nbsp;&nbsp; </span>哎，这些问题是什么时候出现的？我该从什么地方开始？要是在出问题的时候谁能告诉我该有多好啊？没办法，捉bug吧&hellip;&hellip;OK,好了，一切都好了。</div>
<div style="TEXT-INDENT: 21pt">这么模块以前已经编写好得，现在怎么出问题了？</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip;&hellip;</span></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>重复，重复，再重复，疯掉了，这应该是机器做的事情，为什么&hellip;&hellip;恩，编写脚本吧，哦，好像持续集成能够解决这个问题。</div>
<div style="MARGIN: 17pt 0cm 16.5pt"><strong><font size="3"><span>2. </span><span><span>持续集成概述</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>持续集成来源于XP（极限编程）的一个实践。事实上，这个实践早就存在，并且很多并没有实际XP的也在实际着它。</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="2"><span>2.1 </span><span><span>持续集成的优点</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>持续集成最基本的优点就是：持续集成可以减少集成阶段修复bug消耗的时间，从而最终提高生产力。 &nbsp;</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>正如在solar的开发过程中我们所遇到的，因为某个人在工作的时候踩进了别人的领域、影响了别人的代码，而被影响的人还不知道发生了什么，于是bug就出现了。这种bug是最难查的，因为很难有人去特意关注它，随着时间的推移，问题会逐渐恶化。直到有一天，某一不小心触碰到了，然后就是花很多很多的经历去修复。嘿嘿，你现在修复了，说不定下个礼拜这个bug又冒出来了呢。</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="2"><span>2.2 </span><span><span>持续集成的优点</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>如果使用持续集成，这样的bug绝大多数都可以在引入的同一天就被发现。而且，由于一天之中发生变动的部分并不多，所以可以很快找到出错的位置。如果找不到bug究竟在哪里，你也可以不把这些讨厌的代码集成到产品中去。所以，即使在最坏的情况下，你也只是不添加引起bug的特性而已。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>当然，持续集成的排错能力取决于测试技术，你所拥有的测试套件越是详尽，你的系统bug越少，质量越高，排错能力越强。因此，在实施持续集成 之前，你应该要有一套详尽的单元测试套件，当然，如果你采用TDD的方式进行开发那是最好不过了。</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="2"><span>2.3 </span><span><span>持续集成最佳实践</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>持续集成的关键是自动化。绝大多数的集成都可以而且应该自动完成。读取源代码、编译、连接、测试，这些都可以自动完成。最后，你应该得到一条简单的信息，告诉你这次创建是否成功：&ldquo;yes&rdquo;或&ldquo;no&rdquo;。如果成功，本次集成到此为止；如果失败，你应该可以很简单地撤消最后一次的修改，回到前一次成功的创建。在整个创建过程中，完全不需要你动脑子。在solar中，我们采用邮件通知的方式。Martin Fowler 在其文章中提到，持续集成应该： </div>
<div style="MARGIN: 0cm 0cm 0pt 63pt; TEXT-INDENT: -21pt"><span>&sup2;<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>单一代码源</div>
<div style="MARGIN: 0cm 0cm 0pt 63pt; TEXT-INDENT: -21pt"><span>&sup2;<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>自动化创建脚本</div>
<div style="MARGIN: 0cm 0cm 0pt 63pt; TEXT-INDENT: -21pt"><span>&sup2;<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>自测试的代码</div>
<div style="MARGIN: 0cm 0cm 0pt 63pt; TEXT-INDENT: -21pt"><span>&sup2;<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>主创建</div>
<div style="MARGIN: 0cm 0cm 0pt 63pt; TEXT-INDENT: -21pt"><span>&sup2;<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>频繁的提交(Check in)代码</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>更详细的内容请参考Martin Fowler的文章 Continuous Integration</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="2"><span>2.4 </span><span><span>持续集成与日构建</span></span></font></strong></div>
<div style="MARGIN: 0cm 0cm 0pt 90pt"><strong><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>持续集成不是日构建，与日构建建相比它还有以下特点：</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1. </span>持续集成强调了集成频率，和日创建相比，持续集成显得更加频繁。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2. </span>持续集成强调及时反馈，日创建的目的是得到一个可以使用的稳定的发布版本，而持续集成强调的是集成失败之后向开发人员提供快速的反馈，当 然成功创建的结果也是得到稳定的版本。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3. </span>日创建并没有强调开发人员提交（check in）源码的频率，而持续集成鼓励并支持开发人员尽快的提交对源码的修改并得到尽快的反馈。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></div>
<div>从上面明显看出，&ldquo; 频率&rdquo;和&ldquo;反馈&rdquo;这两个词出现的特别多，持 续集成有一个基本要点，那 就是&ldquo; 经常性的集成比偶尔集成要好&rdquo;。Martin Fowler 认为对于持续集成来说，集成越频繁，效果越好 ，如果你的集成不是经常进行的（少于每天一次），那么集成就是一件痛苦的事情，如果集成偶尔才进行一次（一周甚至一个月）， 等到集成阶段发现bug，然后找原因解决bug，会耗费你大量的时间与精力，而且这种方式有点象传统的集成模式，这违背了持续集成的初衷.</div>
<div style="MARGIN: 17pt 0cm 16.5pt"><strong><font size="3"><span>3. Solar </span><span><span>持续集成环境</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Solar</span>的持续集成环境基于CruiseControl搭建。 CruiseControl 原属于thoughtworks公司，后来开放源码，它是一个流行的功能强大的持续集成框架，包括了邮件通知，ant 和各种源码控制工具的插件。并提供了 web 接口，用于查看当前和以前的创建的结果。在下文我们采用cc代替Cruise Control。</div>
<div style="MARGIN: 17pt 0cm 16.5pt"><strong><font size="3"><span>4. CruiseControl</span><span><span>环境搭键</span></span></font></strong></div>
<div style="MARGIN: 13pt 0cm"><strong><span><font size="2">4.1 Build <span>Loop</span></font></span></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>前面在讨论持续集成的时候讲到其最重要的特征之一是自动化，而 CC 的 Build Loop 就是为支持自动化而设计的，Build Loop 也是 CC 的核心。CC 提供了一个 daemon 进程，该进程自动根据配置的时间间隔或指定的某个具体时间读取 CC 配置文件并进行循环创建，每次 CC 都会重新加载配置文件，在CC运行过程中，修改了配置文件不用重新启动 CC。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Build Loop </span>过程中所做的工作如下：访问源码源码控制系统，查看是否有代码被修改，如果有，获取源码的新版本，并根据配置对源码进行一次 Build，创建一个日志文件，最后向开发人员通知 build 的结果，活动图如下：</div>
<div style="MARGIN: 0cm 0cm 0pt 90pt">图1. Build Loop 状态图</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="2"><span>4.2 CC</span><span><span>的配置文件</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cc</span>最主要的配置文件是一个叫做config.xml的配置文件，当然你也可以取其他名字，只要运行cc的时候指定输入文件就行了，类似于ant命令一样。以下是一个示例文件的内容。</div>
<div>
<div class="code_title"><font face="Arial">config.xml</font></div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-xml">
    <li class="alt"><span><span class="tag"><!--sp--><span class="tag-name">xml</span><span>&nbsp;</span><span class="attribute">version</span><span>=</span><span class="attribute-value">&quot;1.0&quot;</span><span>&nbsp;</span><span class="attribute">encoding</span><span>=</span><span class="attribute-value">&quot;GBK&quot;</span><span class="tag">?&gt;</span><span>&nbsp;&nbsp;</span></span> </span></li>
    <li class=""><span></span><span class="comments"><!-- </span> </li>
    <li class="alt"><span><span class="comments">  numen 项目自动化－持续集成配置文件：提供自动化测试，每日构建，持续集成等自动构建 </span> </span></li>
    <li class=""><span><span class="comments"> --></span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span></span><span class="tag">&lt;</span><span class="tag-name">cruisecontrol</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;</span><span class="comments"><!-- Defined public property ! --></span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span></span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">environment</span><span>=</span><span class="attribute-value">&quot;env&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;ant.home&quot;</span><span>&nbsp;</span><span class="attribute">value</span><span>=</span><span class="attribute-value">&quot;${env.ANT_HOME}&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;product.name&quot;</span><span>&nbsp;</span><span class="attribute">value</span><span>=</span><span class="attribute-value">&quot;numen&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span><span class="comments"><!-- Continuous Integration :Run all the plain test suite and command test suite! --></span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">project</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;solar-CI-build&quot;</span><span>&nbsp;</span><span class="attribute">buildafterfailed</span><span>=</span><span class="attribute-value">&quot;false&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">dateformat</span><span>&nbsp;</span><span class="attribute">format</span><span>=</span><span class="attribute-value">&quot;yyyy-MM-dd&nbsp;HH:mm:ss&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">listeners</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">currentbuildstatuslistener</span><span>&nbsp;</span><span class="attribute">file</span><span>=</span><span class="attribute-value">&quot;logs/${project.name}/currentbuildstatus.txt&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">listeners</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bootstrappers</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">currentbuildstatusbootstrapper</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">file</span><span>=</span><span class="attribute-value">&quot;logs/${project.name}/currentbuildstatus.txt&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">bootstrappers</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">modificationset</span><span>&nbsp;</span><span class="attribute">ignoreFiles</span><span>=</span><span class="attribute-value">&quot;*.css,*.js,*.jsp&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">svn</span><span>&nbsp;</span><span class="attribute">localworkingcopy</span><span>=</span><span class="attribute-value">&quot;checkout/${product.name}&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">modificationset</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">schedule</span><span>&nbsp;</span><span class="attribute">interval</span><span>=</span><span class="attribute-value">&quot;7200&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comments"><!-- Continuous Integration :Run all the command test suite! --></span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">anthome</span><span>=</span><span class="attribute-value">&quot;${ant.home}&quot;</span><span>&nbsp;</span><span class="attribute">buildfile</span><span>=</span><span class="attribute-value">&quot;cc-build.xml&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;run.command.tests&quot;</span><span>&nbsp;</span><span class="attribute">multiple</span><span>=</span><span class="attribute-value">&quot;3&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comments"><!-- Continuous Integration :Run all the plain test suite! --></span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">anthome</span><span>=</span><span class="attribute-value">&quot;${ant.home}&quot;</span><span>&nbsp;</span><span class="attribute">buildfile</span><span>=</span><span class="attribute-value">&quot;cc-build.xml&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;run.plain.tests&quot;</span><span>&nbsp;&nbsp;</span><span class="attribute">multiple</span><span>=</span><span class="attribute-value">&quot;1&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">schedule</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">log</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;logs/${project.name}&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">merge</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;checkout/${product.name}/test/report/xml&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">log</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">publishers</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">currentbuildstatuspublisher</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">file</span><span>=</span><span class="attribute-value">&quot;logs/${project.name}/currentbuildstatus.txt&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">artifactspublisher</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;checkout/${product.name}/build&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">dest</span><span>=</span><span class="attribute-value">&quot;artifacts/${project.name}&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">htmlemail</span><span>&nbsp;</span><span class="attribute">mailhost</span><span>=</span><span class="attribute-value">&quot;mail.cenosoft.cn&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">returnaddress</span><span>=</span><span class="attribute-value">&quot;tech@cenosoft.cn&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">defaultsuffix</span><span>=</span><span class="attribute-value">&quot;@cenosoft.cn&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">buildresultsurl</span><span>=</span><span class="attribute-value">&quot;http://192.168.7.236:10000/buildresults/${project.name}&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">css</span><span>=</span><span class="attribute-value">&quot;report/css/cruisecontrol.css&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">xsldir</span><span>=</span><span class="attribute-value">&quot;report/xsl&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">logdir</span><span>=</span><span class="attribute-value">&quot;logs/${project.name}&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">charset</span><span>=</span><span class="attribute-value">&quot;GBK&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">map</span><span>&nbsp;</span><span class="attribute">alias</span><span>=</span><span class="attribute-value">&quot;guyang&quot;</span><span>&nbsp;</span><span class="attribute">address</span><span>=</span><span class="attribute-value">&quot;hyysguyang@gmail.com&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">failure</span><span>&nbsp;</span><span class="attribute">address</span><span>=</span><span class="attribute-value">&quot;guyang&quot;</span><span>&nbsp;</span><span class="attribute">reportWhenFixed</span><span>=</span><span class="attribute-value">&quot;true&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">htmlemail</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">XSLTLogPublisher</span><span>&nbsp;</span><span class="attribute">directory</span><span>=</span><span class="attribute-value">&quot;WebRoot/rss&quot;</span><span>&nbsp;</span><span class="attribute">outfilename</span><span>=</span><span class="attribute-value">&quot;numenbuildstatus.rss&quot;</span><span>&nbsp;</span><span class="attribute">xsltfile</span><span>=</span><span class="attribute-value">&quot;report/rss/xsl/buildstatus.xsl&quot;</span><span>&nbsp;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">publishers</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">project</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="comments"><!-- The nightly build : Run all the test suite and generate code coverage report on two clock! --></span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">project</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;solar-nightly-build&quot;</span><span>&nbsp;</span><span class="attribute">buildafterfailed</span><span>=</span><span class="attribute-value">&quot;false&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">dateformat</span><span>&nbsp;</span><span class="attribute">format</span><span>=</span><span class="attribute-value">&quot;yyyy-MM-dd&nbsp;HH:mm:ss&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">listeners</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">currentbuildstatuslistener</span><span>&nbsp;</span><span class="attribute">file</span><span>=</span><span class="attribute-value">&quot;logs/${project.name}/currentbuildstatus.txt&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">listeners</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">bootstrappers</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">currentbuildstatusbootstrapper</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">file</span><span>=</span><span class="attribute-value">&quot;logs/${project.name}/currentbuildstatus.txt&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">bootstrappers</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">modificationset</span><span>&nbsp;</span><span class="attribute">requiremodification</span><span>=</span><span class="attribute-value">&quot;false&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">svn</span><span>&nbsp;</span><span class="attribute">localworkingcopy</span><span>=</span><span class="attribute-value">&quot;checkout/${product.name}&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">modificationset</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">schedule</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">anthome</span><span>=</span><span class="attribute-value">&quot;${ant.home}&quot;</span><span>&nbsp;</span><span class="attribute">buildfile</span><span>=</span><span class="attribute-value">&quot;cc-build.xml&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;run.all&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">time</span><span>=</span><span class="attribute-value">&quot;0300&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">schedule</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">log</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;logs/${project.name}&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">merge</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;checkout/${product.name}/test/report/xml&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">log</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">publishers</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">currentbuildstatuspublisher</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">file</span><span>=</span><span class="attribute-value">&quot;logs/${project.name}/currentbuildstatus.txt&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">artifactspublisher</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;checkout/${product.name}/build&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">dest</span><span>=</span><span class="attribute-value">&quot;artifacts/${project.name}&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">htmlemail</span><span>&nbsp;</span><span class="attribute">mailhost</span><span>=</span><span class="attribute-value">&quot;mail.cenosoft.cn&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">returnaddress</span><span>=</span><span class="attribute-value">&quot;tech@cenosoft.cn&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">defaultsuffix</span><span>=</span><span class="attribute-value">&quot;@cenosoft.cn&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">buildresultsurl</span><span>=</span><span class="attribute-value">&quot;http://localhost:10000/buildresults/${project.name}&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">css</span><span>=</span><span class="attribute-value">&quot;report/css/cruisecontrol.css&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">xsldir</span><span>=</span><span class="attribute-value">&quot;report/xsl&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">logdir</span><span>=</span><span class="attribute-value">&quot;logs/${project.name}&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">charset</span><span>=</span><span class="attribute-value">&quot;GBK&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">map</span><span>&nbsp;</span><span class="attribute">alias</span><span>=</span><span class="attribute-value">&quot;guyang&quot;</span><span>&nbsp;</span><span class="attribute">address</span><span>=</span><span class="attribute-value">&quot;hyysguyang@gmail.com&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">failure</span><span>&nbsp;</span><span class="attribute">address</span><span>=</span><span class="attribute-value">&quot;guyang&quot;</span><span>&nbsp;</span><span class="attribute">reportWhenFixed</span><span>=</span><span class="attribute-value">&quot;true&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">htmlemail</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">XSLTLogPublisher</span><span>&nbsp;</span><span class="attribute">directory</span><span>=</span><span class="attribute-value">&quot;WebRoot/rss&quot;</span><span>&nbsp;</span><span class="attribute">outfilename</span><span>=</span><span class="attribute-value">&quot;numenbuildstatus.rss&quot;</span><span>&nbsp;</span><span class="attribute">xsltfile</span><span>=</span><span class="attribute-value">&quot;report/rss/xsl/buildstatus.xsl&quot;</span><span>&nbsp;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">publishers</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">project</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span></span><span class="tag"><span class="tag-name">cruisecontrol</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
</ol>
</div>
</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="2"><span>4.3 </span><span><span>配置示例</span></span></font></strong></div>
<div style="MARGIN: 0cm 0cm 0pt 42pt; TEXT-INDENT: -21pt"><span>&Oslash;<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>下载CC</div>
<div style="MARGIN: 0cm 0cm 0pt 42pt; TEXT-INDENT: -21pt"><span>&Oslash;<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>Build CC</div>
<div style="MARGIN: 0cm 0cm 0pt 42pt; TEXT-INDENT: -21pt"><span>&Oslash;<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>建立build结构</div>
<div style="MARGIN: 0cm 0cm 0pt 42pt; TEXT-INDENT: -21pt"><span>&Oslash;<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>编写config.xml</div>
<div style="MARGIN: 0cm 0cm 0pt 42pt; TEXT-INDENT: -21pt"><span>&Oslash;<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>引入wrapper buildfile</div>
<div style="MARGIN: 0cm 0cm 0pt 42pt; TEXT-INDENT: -21pt"><span>&Oslash;<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>Report Result</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="1"><span><span>4.3.1</span></span><span><span>下载CC</span></span></font></strong></div>
<div>&nbsp;<span>&nbsp;&nbsp;&nbsp; </span>你可以从下面的连接中下载cc的源码包或者binary包。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CC Download</span></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Source Distribution </span></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Binary Distribution </span></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>解压缩到某一个目录，如D:\cruisecontrol-2.5，以下我们用 CC_HOME 来引用这个目录，将％CC_HOME％\main\bin添加到系统的%PATH%环境变量中去。</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="1"><span><span>4.3.2</span></span><span><span> Build CC</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp; %CC_HOME%</span>目录下的结构如下：</div>
<div>&nbsp;</div>
<div align="center">图2. CC目录结构</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="1"><span><span>4.3.3</span></span><span><span>创建 cruisecontrol.jar</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>运行 %CC_HOME%\main\build.bat ， 该命令会编译 ， 测试 ， 并创建%CC_HOME%\main\dist\cruisecontrol.jar。现在即可通过以下方式运行 %CC_HOME%\main\bin\cruisecontrol.bat ，</div>
<div>%CC_HOME%\main\dist\java jar cruisecontrol.jar</div>
<div>或者在命令行输入cruisecontrol命令。</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="1"><span><span>4.3.4</span></span><span><span>创建 cruisecontrol.war</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>在创建 cruisecontrol.war 之前，我们需要传递三个参数给创建程序，分别用来指定 CC 产生</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>的日志的目录（user.log.dir），创建过程中产生的状态文件的名字（user.build.status.file），还有创建过程中输出的人工制品所在的目录（cruise.build.artifacts.dir）。假如我们的build home为/home，则其对应的值如下：</div>
<div style="MARGIN: 0cm 0cm 0pt 63pt">user.log.dir=/home/logs</div>
<div style="MARGIN: 0cm 0cm 0pt 63pt">user.build.status.file=currentbuildstatus.txt</div>
<div style="MARGIN: 0cm 0cm 0pt 63pt">cruise.build.artifacts.dir=/home/artifacts</div>
<div style="TEXT-INDENT: 21pt">转到%CC_HOME%\reporting\jsp\，然后运行以下命令：</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; build -Duser.log.dir=/home/logs</span></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -Duser.build.status.file=status.txt </span></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -Dcruise.build.artifacts.dir=/home/artifacts</span></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>当然你可以不指定这三个参数，仅仅运行build，然后会提示你输入这三个参数的。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>另外一种方法，就是在该目录下创建一个新文件 override.properties，文件内容如下，在改文件中指定这三个属性，然后build，就可以了，如。user.log.dir=/home/logs</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user.build.status.file=status.txt</span></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cruise.build.artifacts.dir=/home/artifacts</span></div>
<div style="MARGIN: 13pt 0cm"><strong><font size="1"><span><span>4.3.5</span></span><span><span>建立build</span></span><span><span>结构</span></span></font></strong></div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div align="center">图3. CC Build结构</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; artifacts </span>目录：存放CC 在 build loop 过程中产生的人工制品，如JAR，对于我们的solar就是每一次集成build的solar.jar,solar-test.jar</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; checkout </span>目录：作为 CC 从源码控制系统检出</div>
<div>的项目存放的目录，对于我们的solar就是从svn上检出存放的目录，当然这里可以存放多个项目。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Logs </span>目录：CC 的 build loop 过程中产生日志所在的目录</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="1"><span><span>4.3.6</span></span><span><span>编写config.xml</span></span></font></strong></div>
<div style="TEXT-INDENT: 21pt">参见上文的config.xml文件。下面解释各个节点，更详细的内容请查看官方文档。</div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt"><currentbuildstatusbootstrapper></currentbuildstatusbootstrapper>:设置项目状态文件</div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt"><modificationset></modificationset></div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt">quietperiod属性：开始build时间与实际build时间的间隔</div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt">ignoreFiles:在svn变化时忽略变化的文件类型</div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt">ignoreFiles=&quot;*.css,*.js,*.xsp,*.xal&ldquo;</div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt"><schedule></schedule>调度build</div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt"><log></log>合并日志的</div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt"><merge></merge>合并日志</div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt">
<publishers></publishers>
:发布build结果</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="1"><span><span>4.3.7</span></span><span><span>引入wrapper buildfile</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>由于我们是基于ant进行构建的，当build文件变化时，我们应该先要update build.xml,你可以在bootstrappers的时候update这个文件当然，你也可以采用类似的方式update类似的文件。</div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt"><bootstrappers></bootstrappers></div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt"><span>&nbsp;&nbsp;&nbsp; <svnbootstrapper span=""></svnbootstrapper></span></div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; file=&ldquo;checkout/solar/build.xml&quot; /&gt;</span></div>
<div style="MARGIN: 0cm 0cm 0pt 52.5pt"></div>
<div style="TEXT-INDENT: 21pt">此外，还有一种替代方法是使用 wrapper buildfile，这样就不用使用<cvsbootstrapper></cvsbootstrapper>了。对应上文的config.xml文件的wrapper buildfile如下：</div>
<div>
<div class="code_title"><font face="Arial">cc-build.xml</font></div>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-xml">
    <li class="alt"><span><span class="tag"><!--sp--><span class="tag-name">xml</span><span>&nbsp;</span><span class="attribute">version</span><span>=</span><span class="attribute-value">&quot;1.0&quot;</span><span>&nbsp;</span><span class="attribute">encoding</span><span>=</span><span class="attribute-value">&quot;GBK&quot;</span><span class="tag">?&gt;</span><span>&nbsp;&nbsp;</span></span> </span></li>
    <li class=""><span></span><span class="comments"><!-- </span> </li>
    <li class="alt"><span><span class="comments"> numen 项目自动化－持续集成基于ant的自动构建包装build.xml </span> </span></li>
    <li class=""><span><span class="comments">--></span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span></span><span class="tag">&lt;</span><span class="tag-name">project</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;cc-build&quot;</span><span>&nbsp;</span><span class="attribute">default</span><span>=</span><span class="attribute-value">&quot;build&quot;</span><span>&nbsp;</span><span class="attribute">basedir</span><span>=</span><span class="attribute-value">&quot;.&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">property</span><span>&nbsp;</span><span class="attribute">file</span><span>=</span><span class="attribute-value">&quot;cc-build.properties&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">taskdef</span><span>&nbsp;</span><span class="attribute">resource</span><span>=</span><span class="attribute-value">&quot;svntask.properties&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">target</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;update.resource&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">svn</span><span>&nbsp;</span><span class="attribute">javahl</span><span>=</span><span class="attribute-value">&quot;false&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">update</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;${numen_base_dir}&quot;</span><span>&nbsp;</span><span class="attribute">revision</span><span>=</span><span class="attribute-value">&quot;head&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">svn</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">target</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">target</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;build&quot;</span><span>&nbsp;</span><span class="attribute">depends</span><span>=</span><span class="attribute-value">&quot;update.resource&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">antfile</span><span>=</span><span class="attribute-value">&quot;build.xml&quot;</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;${numen_base_dir}&quot;</span><span>&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;clean&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">antfile</span><span>=</span><span class="attribute-value">&quot;build.xml&quot;</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;${numen_base_dir}&quot;</span><span>&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;build&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">target</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">target</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;run.plain.tests&quot;</span><span>&nbsp;</span><span class="attribute">depends</span><span>=</span><span class="attribute-value">&quot;update.resource&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">antfile</span><span>=</span><span class="attribute-value">&quot;build.xml&quot;</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;${numen_base_dir}&quot;</span><span>&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;clean&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">antfile</span><span>=</span><span class="attribute-value">&quot;build.xml&quot;</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;${numen_base_dir}&quot;</span><span>&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;run.plain.tests&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">target</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">target</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;run.command.tests&quot;</span><span>&nbsp;</span><span class="attribute">depends</span><span>=</span><span class="attribute-value">&quot;update.resource&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">antfile</span><span>=</span><span class="attribute-value">&quot;build.xml&quot;</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;${numen_base_dir}&quot;</span><span>&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;clean&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">antfile</span><span>=</span><span class="attribute-value">&quot;build.xml&quot;</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;${numen_base_dir}&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;run.portal.command.tests&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">antfile</span><span>=</span><span class="attribute-value">&quot;build.xml&quot;</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;${numen_base_dir}&quot;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;run.admin.command.tests&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">target</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">target</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;run.all&quot;</span><span>&nbsp;</span><span class="attribute">depends</span><span>=</span><span class="attribute-value">&quot;update.resource&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">antfile</span><span>=</span><span class="attribute-value">&quot;build.xml&quot;</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;${numen_base_dir}&quot;</span><span>&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;clean&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">ant</span><span>&nbsp;</span><span class="attribute">antfile</span><span>=</span><span class="attribute-value">&quot;build.xml&quot;</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;${numen_base_dir}&quot;</span><span>&nbsp;</span><span class="attribute">target</span><span>=</span><span class="attribute-value">&quot;clover.html&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">copy</span><span>&nbsp;</span><span class="attribute">todir</span><span>=</span><span class="attribute-value">&quot;${webapp_root_dir}/clover&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">fileset</span><span>&nbsp;</span><span class="attribute">dir</span><span>=</span><span class="attribute-value">&quot;${solar_base_dir}/test/clover/html&quot;</span><span>&nbsp;</span><span class="attribute">includes</span><span>=</span><span class="attribute-value">&quot;**&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span> </li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">copy</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag"><span class="tag-name">target</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
    <li class=""><span>&nbsp;&nbsp;</span> </li>
    <li class="alt"><span></span><span class="tag"><span class="tag-name">project</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span> </span></li>
</ol>
</div>
</div>
<div>&nbsp;</div>
<div style="MARGIN: 13pt 0cm"><strong><font size="1"><span><span>4.3.8</span></span><span><span> Report Result</span></span></font></strong></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>要发布构建结果很简单，只要cruisecontrol.war放到任意一个web 容器中即可浏览cc的构建结果。</div>
<div style="MARGIN: 17pt 0cm 16.5pt"><strong><font size="3"><span>5. </span><span><span>小结</span></span></font></strong></div>
<div style="TEXT-INDENT: 21pt">持续集成有很多好处，可以大量降低集成时间，此外更最重要的是它可以迅速反馈，给我们勇气和信心，当然这个是建立在详尽的单元测试的基础上的。显然持续集成已经不像以前那样只是挂在嘴巴的高深的名词，只要你愿意你都可以实施持续集成，然而你还是需要遵循某些原则，遵循这些原则比持续集本身更重要。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1. </span>频繁的commit.</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2. </span>确保编译通过，确保所有测试都通过。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3. </span>及时反馈，让前一次构建发现的bug不要在下一次重复出现。如果你是一个优秀的开发人员，相信你不会让某个bug驻留太久。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4. </span>测试你的代码，建立详尽的单元测试suite，</div>
<div><span>&nbsp;&nbsp; </span>总之，持续集成仅仅是我们项目过程中的一个工具而已，正如scm工具、单元测试一样。<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>工具就是工具，人比什么都重要，正如XP推崇的。</div>
<div style="MARGIN: 17pt 0cm 16.5pt"><strong><font size="3"><span>6. </span><span><span>参考文献</span></span></font></strong></div>
<div>1．<a href="http://www.martinfowler.com/articles/continuousIntegration.html">http://www.martinfowler.com/articles/continuousIntegration.html</a></div>
<div>2．<a href="http://cruisecontrol.sourceforge.net/">http://cruisecontrol.sourceforge.net/</a></div>
          <br/>
          <span style="color:red;">
            <a href="http://hyysguyang.javaeye.com/blog/38610#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 11 Dec 2006 22:37:58 +0800</pubDate>
        <link>http://hyysguyang.javaeye.com/blog/38610</link>
        <guid>http://hyysguyang.javaeye.com/blog/38610</guid>
      </item>
      <item>
        <title>开发利器之单元测试</title>
        <author>hyysguyang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hyysguyang.javaeye.com">hyysguyang</a>&nbsp;
          链接：<a href="http://hyysguyang.javaeye.com/blog/38479" style="color:red;">http://hyysguyang.javaeye.com/blog/38479</a>&nbsp;
          发表时间: 2006年12月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>这是以前一次新员工单元测试的培训文档，只可惜那次的收效并不大，不过对我来说却是很有帮助，促使我对单元测试做了一次很好的总结，以前比较零散，只是在记忆中而已。</p>
<div align="center"><strong><span style="font-size: 22pt;"><font size="5">开发利器之单元测试</font></span></strong></div>
<div align="center"><span style="font-size: 9pt;">hyysguyang 2006-08-30</span></div>
<div><span>0<span><span>．导言</span></span></span></div>
<div><span>1.<span><span>单元测试的分类</span></span></span></div>
<div style="margin: 0cm 0cm 0pt 21pt;"><span>1.1 <span><span>逻辑单元测试（</span>plain junit test<span>）</span></span></span></div>
<div style="margin: 0cm 0cm 0pt 21pt;"><span>1.2. <span><span>集成单元测试</span></span></span></div>
<div style="margin: 0cm 0cm 0pt 21pt;"><span>1.3. <span><span>功能单元测试</span></span></span></div>
<div><span>2.<span><span>单元测试的动机</span></span></span></div>
<div><span>3.<span><span>单元测试的目标</span></span></span></div>
<div><span>4.<span><span>确保可测试性</span></span></span></div>
<div><span>5.<span><span>测试策略</span></span></span></div>
<div style="margin: 0cm 0cm 0pt 21pt;"><span>5.1<span><span>用</span>stub<span>进行粗粒度测试</span></span></span></div>
<div style="margin: 0cm 0cm 0pt 21pt;"><span>5.2<span><span>用</span>mock objects<span>进行孤立测试</span></span></span></div>
<div style="margin: 0cm 0cm 0pt 21pt;"><span>5.3<span><span>用</span>Cactus<span>进行容<span>器<span>内测试</span></span></span></span></span></div>
<div><span>6 <span><span>测试覆盖率</span></span></span></div>
<div><span>7.<span><span>最佳实践</span></span></span></div>
<div><span>8.Spring<span><span>的经验</span></span></span></div>
<div><span>9.<span><span>参考资料</span></span></span></div>
<div>&nbsp;</div>
<div style="margin: 17pt 0cm 16.5pt;"><strong><font size="3"><a name="_Toc153600093">0</a><span>．导言</span></font></strong></div>
<div style="text-indent: 21pt;">Never in the field of software development was so much owed by so many to so few lines of code.</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>软件开发领域中此前从未有过这样的事情：很少几行代码对大量的代码起了重要的作用。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&mdash;&mdash;Martin Fowler</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Any program feature without an automated test simply doesn&rsquo;t exist.</span></div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>任何没有经过自动测试的程序特性就等于不存在的特性。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&mdash;&mdash; Kent Beck</div>
<div align="center">&nbsp;</div>
<div style="margin: 17pt 0cm 16.5pt;"><strong><font size="3"><a name="_Toc153600094"><span style="line-height: 240%;">1.</span></a>&nbsp;<span><span style="line-height: 240%;">单元测试的分类</span></span></font></strong></div>
<div style="margin: 13pt 0cm;"><strong><font size="1"><a name="_Toc153600095"><span style="line-height: 173%;">1.1 </span></a>&nbsp;<span><span style="line-height: 173%;">逻辑单元测试（</span></span><span><span style="line-height: 173%;">plain junit test</span></span><span><span style="line-height: 173%;">）</span></span></font></strong></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">逻辑单元测试主要检查代码逻辑性，这些测试通常只是针对单个方法。你可以通过</span><span style="font-size: 9pt;">mock </span></div>
<div><span style="font-size: 9pt;">objects </span><span style="font-size: 9pt;">或者</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">来控制特定的方法的边界。这种类型的单元测试是最重要也是最基本的。其在单元测试中的重要性不亚于</span><span style="font-size: 9pt;">POJO.</span><span style="font-size: 9pt;">这种类型的测试最大的特点就是执行速度快，而这也是单元测试最重要的一个特性之一。</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">可以参看附近中的文件过滤器</span><span style="font-size: 9pt;">ExtensionFileFilter.java</span><span style="font-size: 9pt;">及其测试用例。</span></div>
<div style="margin: 13pt 0cm;"><strong><font size="1"><a name="_Toc153600096"><span style="line-height: 173%;">1.2. </span></a><span><span style="line-height: 173%;"></span></span>集成单元测试</font></strong></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">这种单元测试主要是在真是环境（或者真实的环境的一部分）下的两个组件相互交互的测试。例如：一段访问数据库的程序已经被测试证实能够有效地访问到数据库，那么就可以提供和数据库交互的接口</span><span style="font-size: 9pt;">,</span><span style="font-size: 9pt;">最典型的就是</span><span style="font-size: 9pt;">Cactus</span><span style="font-size: 9pt;">容器内测试，以及公司的测试</span><span style="font-size: 9pt;">command</span><span style="font-size: 9pt;">的</span><span style="font-size: 9pt;">Lavender</span><span style="font-size: 9pt;">已经测试非</span><span style="font-size: 9pt;">command</span><span style="font-size: 9pt;">组件的</span><span style="font-size: 9pt;">vanilla</span><span style="font-size: 9pt;">。</span><span style="font-size: 9pt;">commad</span><span style="font-size: 9pt;">的测试实例可以参看附近中的规则命令</span><span style="font-size: 9pt;">SolarRuleEntryCommand.java</span><span style="font-size: 9pt;">及其测试用例。</span></div>
<div style="margin: 13pt 0cm;"><strong><font size="1"><a name="_Toc153600097"><span style="line-height: 173%;">1.</span>3. </a><span>功能单元测试</span></font></strong></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">这种单元测试越出了集成单元测试的边界，目的是为了确认激励－响应。如访问页面测试。</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">更相信的分类可以参考相关的测试文献，</span><span style="font-size: 9pt;">junit in action </span><span style="font-size: 9pt;">上面也提到，事实上上面提到的概念，都来自于它。</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">没有什么是最好的，最适合的就是最好的！写单元测试你不得不写额外的代码，如果写测试没有好处，我们又何必去浪费时间呢。因此你应该确信测试用例能给你带来好处，这样你才会愿意去写，也只有这样你才能写的出对于你有帮助的测试用例。因此，在你未确信单元测试给你带来好处之前，请不要去写它。既然这样，我们就简单看看单元测试到底给我们带来什么好处。</span></div>
<div>&nbsp;</div>
<div style="margin: 17pt 0cm 16.5pt;"><strong><font size="3"><a name="_Toc153600098"><span style="line-height: 240%;">2.</span></a><span><span style="line-height: 240%;">单元测试的动机</span></span></font></strong></div>
<div style="margin: 13pt 0cm;"><strong><font size="1"><a name="_Toc153600099"><span style="font-size: 9pt; line-height: 173%;">2.1. </span></a><span><span style="font-size: 9pt; line-height: 173%;"></span></span>确保产品质量，单元测试可以有效的挑出很多常见的代码错误。</font></strong></div>
<div style="margin: 13pt 0cm;"><strong><font size="1"><a name="_Toc153600100"><span style="font-size: 9pt; line-height: 173%;">2.2. </span></a><span><span style="font-size: 9pt; line-height: 173%;"></span></span><span><span style="font-size: 9pt; line-height: 173%;">&nbsp;</span></span><span><span style="font-size: 9pt; line-height: 173%;">一边编写应用代码一边编写测试，有助于定义类的需求。</span></span>有助于明确需求，</font></strong></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">如对于一个方法，对于传入的</span><span style="font-size: 9pt;">null</span><span style="font-size: 9pt;">参数应该如何处理？是否应该返回</span><span style="font-size: 9pt;">null?</span><span style="font-size: 9pt;">你必须为这些情况编写测试，所以你一定得把这些行为都想得清清楚楚，之后就可以在</span><span style="font-size: 9pt;">JavaDoc</span><span style="font-size: 9pt;">中写明它得用法。</span></div>
<div style="margin: 13pt 0cm;"><strong><font size="1"><a name="_Toc153600101"><span style="font-size: 9pt; line-height: 173%;">2.3. </span></a><span><span style="font-size: 9pt; line-height: 173%;"></span></span><span><span style="font-size: 9pt; line-height: 173%;">(Test Suite)</span></span><span><span style="font-size: 9pt; line-height: 173%;">是一种强大而重要得文档。可执行得文档，保持与代码同步。</span></span>测试套件</font></strong></div>
<div style="margin: 13pt 0cm;"><strong><font size="1"><a name="_Toc153600102"><span style="font-size: 9pt; line-height: 173%;">2.4. </span></a><span><span style="font-size: 9pt; line-height: 173%;"></span></span><span><span style="font-size: 9pt; line-height: 173%;">Test Suite</span></span><span><span style="font-size: 9pt; line-height: 173%;">确保有效的回归测试。</span></span>详尽的</font></strong></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">当我们增加了新功能，或者修正</span><span style="font-size: 9pt;">bug</span><span style="font-size: 9pt;">时，我们可以确保现存的功能仍然正确。同时增强自信。确保不断疯狂的重构。重构保证我们不管在什么时候对所需的功能有最优的实现。优秀的开发人员都习惯于重构。每次我</span><span style="font-size: 9pt;">commit</span><span style="font-size: 9pt;">到</span><span style="font-size: 9pt;">SCM(</span><span style="font-size: 9pt;">如公司采用的</span><span style="font-size: 9pt;">svn)</span><span style="font-size: 9pt;">中的代码都是正确的，因为在</span><span style="font-size: 9pt;">commit</span><span style="font-size: 9pt;">之前我看到了绿色动态条，我的所有的代码都通过了测试，这很好。这样你难道不会更加自信么？</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">对于重构的精妙叙述请参阅</span><span style="font-size: 9pt;">Martin Fowler </span><span style="font-size: 9pt;">的经典重构</span><span style="font-size: 9pt;">:</span><span style="font-size: 9pt;">改善既有代码的设计</span><span style="font-size: 9pt;">.</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">就上述几点，我相信测试用例就值得我去写。其实还有很多其他的好处，但目前来说我体会最深的就是这几点。既然单元测试有这么多好处，那就让我们看看，我们该测什么？什么时候我们需要写测试用例呢？</span></div>
<div style="margin: 17pt 0cm 16.5pt;"><strong><font size="3"><a name="_Toc153600103"><span style="line-height: 240%;">3.</span></a><span><span style="line-height: 240%;">单元测试的目标</span></span></font></strong></div>
<div>&nbsp;</div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">其实编写单元测试很简单，不论是</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">也好，还是</span><span style="font-size: 9pt;">mock object</span><span style="font-size: 9pt;">也好，</span></div>
<div><span style="font-size: 9pt;">抑或是容器内测试也好，入门都很简单，更重要的是测试策略，针对什么类你采用对应的测试方法，是</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">呢还是</span><span style="font-size: 9pt;">mock object</span><span style="font-size: 9pt;">，你应该知道你该测什么，而不该测什么，这才是最重要的，当然</span></div>
<div><span style="font-size: 9pt;">这些需要不断的实践，经验越丰富你就越清楚。</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">单元测试一般是白盒测试，通常需要对被测试的类的内部细节有足够的了解。在写测试用例时请永远记住：</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; 1. </span></span><span style="font-size: 9pt;">每一个测试用例只应该测试一个类，而不是间接测试其合作者。</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">因为没个需要测试的类都会有自己的测试用例，它不再需要其他的来间接测试它。</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; 2. </span></span><span style="font-size: 9pt;">基于第一条，你应对你的类进行隔离测试。采用</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">，或者</span><span style="font-size: 9pt;">mock object</span><span style="font-size: 9pt;">，替代你要测的类的协作对象。把你要测的类隔离开来进行测试。</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">单元测试也意味着你要知道哪些是不应该被测试的。当你明白哪些是不该测试的，剩下哪些该测试就显而易见了。明确该测什么不该测什么，这点很重要。</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">对于什么该测，什么不该测试，以前我及其迷惘，因为我不知道某个类我到底该不该测，思来想去之后，我后面决定：</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">任何时候对你的任何代码不放心，请你立刻写一个对应的测试。确定测试通过之后，直到你放心为止。</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">以我的经历，写测试用例不难，但写有用的测试用例就不是那么容易了。提高你的测试能力一个比较好的做法就是动手去写，分享别人的经验，实践一些最佳实践。</span></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">既然我们已经确认我们应该要写单元测试了，那么就存在一个问题：这个类容易测试么？</span><span style="font-size: 9pt;">可测试么？这就涉及代码可测试性的问题，在下一节，我们就来看看代码的可测试性吧。</span></div>
<div>&nbsp;</div>
<div style="margin: 17pt 0cm 16.5pt;"><strong><font size="3"><a name="_Toc153600104"><span style="line-height: 240%;">4.</span></a><span><span style="line-height: 240%;">确保可测试性</span></span></font></strong></div>
<div><span style="font-size: 9pt;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">不同的代码（类或方法）其可测试性是有很大区别的，比如：给你一个计算</span><span style="font-size: 9pt;">a</span><span style="font-size: 9pt;">＋</span><span style="font-size: 9pt;">b</span><span style="font-size: 9pt;">的方法，相信你很快就写好它的测试代码，可是如果让你测试一个</span><span style="font-size: 9pt;">servlet</span><span style="font-size: 9pt;">或访问数据库的代码呢？也许你就要花上一点时间了。</span><span style="font-size: 9pt;">比如，对于</span><span style="font-size: 9pt;">EJB</span><span style="font-size: 9pt;">组件必须在应用服务器中运行，要测他也许你要启动一个应用服务器，且要部署整个应用，要编写并部署这样一个组件的一个测试用例就会花费很长的时间，运行这样的测试用例同样要花费很长很长的时间，这样你就不能很频繁的运行测试套件，你也就失去很多单元测试所带来的好处。</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">既然我们要编写单元测试，那可测试性就理所当然的成为程序代码质量的一个重要方面。事实上代码的可测试性与程序代码的质量有很大的关联。</span></div>
<div><span style="font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt;">容易测试的代码通常会更好、更可读、并且更加易于维护。对于难测试的代码通常也难于维护和升级。改善代码的可测试性很大程度上会改善你的设计。大多数情况下，编写可测试的程序代码会促成高质量的程序代码。比如：</span></div>
<div><span style="font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1</span><span style="font-size: 9pt;">．如果一个方法长的无法确保其中所有的代码都被执行了，那么这个方法必定也是不可读的、难于维护的。</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">2</span><span style="font-size: 9pt;">．如果有些代码依赖于在</span><span style="font-size: 9pt;">Singleton</span><span style="font-size: 9pt;">中持有的全局静态状态，这些代码无法独立进行测试。</span></div>
<div><span style="font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3</span><span style="font-size: 9pt;">．假若代码不是可插入的&mdash;&mdash;也就是说无法把一个特定的合作者替换成一个模仿测试对象</span><span style="font-size: 9pt;">&mdash;</span><span style="font-size: 9pt;">会使的程序难以扩张。</span></div>
<div><span style="font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt;">如果你不知道如何测试某段代码，请考虑重构，看看是否能对实现略加修改以便进行测试。</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">有些编程惯例容易导致不可测试的代码，如：</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt;"><span style="font-size: 9pt;">1．&nbsp;</span><span style="font-size: 9pt;">Singleton</span><span style="font-size: 9pt;">反模式</span></div>
<div style="margin: 0cm 0cm 0pt 18pt; text-indent: 21pt;"><span style="font-size: 9pt;">难以用</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">来替换</span><span style="font-size: 9pt;">Singleton</span><span style="font-size: 9pt;">对象。无法被</span><span style="font-size: 9pt;">IoC</span><span style="font-size: 9pt;">容器创建和管理。</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -18pt;"><span style="font-size: 9pt;">2．&nbsp;</span><span style="font-size: 9pt;">静态</span><span style="font-size: 9pt;">Fa&ccedil;ade</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt;">由静态方法组成的</span><span style="font-size: 9pt;">fa&ccedil;ade</span><span style="font-size: 9pt;">也不容易测试。由于所有的调用者都绑定到了静态</span><span style="font-size: 9pt;">Fa&ccedil;ade</span><span style="font-size: 9pt;">，你不可能插入一个测试实现。此外，静态方法无法被覆盖，这样实际上失去了一种重要的</span><span style="font-size: 9pt;">OOP</span><span style="font-size: 9pt;">能力。</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">一般只有在下列情况下才应该使用静态方法。</span></div>
<div style="margin: 0cm 0cm 0pt 36pt; text-indent: -18pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">不属于任何类的、过程性的工具方法。</span></div>
<div style="margin: 0cm 0cm 0pt 36pt; text-indent: -18pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">操作</span><span style="font-size: 9pt;">ThreadLocal</span><span style="font-size: 9pt;">状态。</span></div>
<div style="margin: 0cm 0cm 0pt 36pt; text-indent: -18pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">返回</span><span style="font-size: 9pt;">Singleton</span><span style="font-size: 9pt;">的实例。</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">遵循一定的原则和利用一些技巧可以提高代码的可测试性，如：</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">针对接口编程，而非针对类编程</span></div>
<div style="margin: 0cm 0cm 0pt 75pt; text-indent: -57pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">使用</span><span style="font-size: 9pt;">Strategy</span><span style="font-size: 9pt;">设计模式</span></div>
<div style="margin: 0cm 0cm 0pt 75pt; text-indent: -57pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">牢记迪米特法则</span></div>
<div style="margin: 0cm 0cm 0pt 75pt; text-indent: -57pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">尽量减少对环境相关</span><span style="font-size: 9pt;">API</span><span style="font-size: 9pt;">的依赖</span></div>
<div style="margin: 0cm 0cm 0pt 75pt; text-indent: -57pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">合理划分类的职责</span></div>
<div style="margin: 0cm 0cm 0pt 75pt; text-indent: -57pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">隐藏实现细节</span></div>
<div style="margin: 0cm 0cm 0pt 75pt; text-indent: -57pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">重构以便在测试期间覆盖某些方法</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">从上面的几点可以看出，编写容易测试的代码与编写优秀的高质量的代码所遵循的编程惯例是一致的。事实上先编写测试用例再编写产品代码（或者采用</span><span style="font-size: 9pt;">TDD</span><span style="font-size: 9pt;">）的方式进行开发逼着你遵循这些原则，很自然的，你在不经意之中，就遵循了这些优秀的编程管理，当然实际上不仅仅这些，还有很多其他的</span><span style="font-size: 9pt;">OOD</span><span style="font-size: 9pt;">原则，都与上述的这些类似。</span></div>
<div style="margin: 17pt 0cm 16.5pt;"><strong><font size="3"><a name="_Toc153600105"><span style="line-height: 240%;">5.</span></a><span>测试策略</span></font></strong></div>
<div><span style="font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt;">并不是每个类都可以用同样的方式来测试的，比如测试访问数据库的代码，测试依赖与容器的组件</span><span style="font-size: 9pt;">(</span><span style="font-size: 9pt;">如</span><span style="font-size: 9pt;">Servlet,EJB),</span><span style="font-size: 9pt;">因此需要针对具体要测试的类采用相应的测试方式。下面是一般测试策略。</span></div>
<div style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">用</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">进行粗粒度测试</span></div>
<div style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">用</span><span style="font-size: 9pt;">mock objects</span><span style="font-size: 9pt;">进行孤立测试</span></div>
<div style="margin: 0cm 0cm 0pt 42pt; text-indent: -21pt;"><span style="font-size: 9pt;">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">用</span><span style="font-size: 9pt;">Cactus</span><span style="font-size: 9pt;">进行容器内测试</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">前两种其实都是为了解决同一个问题：如何保持测试的独立性，而最后一种测试实际上属于集成单元测试。</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">在单元测试的目标中，我们提到，每一个测试用例只应该测试一个类，而不是间接测试其合作者。既然这样，那这个类的合作者我们怎么去创建呢？很多时候他们类代码我们也许还没编写，而我们现在用用，该怎么办？采用</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">或者</span><span style="font-size: 9pt;">mock object</span><span style="font-size: 9pt;">进行隔离测试。</span></div>
<div style="margin: 13pt 0cm;"><strong><font size="1"><a name="_Toc153600106">5.1</a><span>用stub</span><span>进行粗粒度测试</span></font></strong></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;"><span>&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">一般来说，你都可以编写你所测试的类所依赖的其他类的</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">来替代协作的对象。不过这样你可能就需要编写大量的</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">代码。而且有些时候编写这样的</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">并不是那么容易，如当你测试一个</span><span style="font-size: 9pt;">EJB</span><span style="font-size: 9pt;">组件是，你可能需要实现一个简单的</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">容器。</span></div>
<div style="margin: 13pt 0cm;"><strong><font size="1"><a name="_Toc153600107">5.2</a><span>用mock objects</span><span>进行孤立测试</span></font></strong></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;"><span>&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">除了采用</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">之外，你还可以写一个模拟协作类的类，用他创建的对象来代替要测试的类的协作对象，我们称这样的对象为</span><span style="font-size: 9pt;">mock object</span><span style="font-size: 9pt;">（模拟对象）。模拟对象有两种方式：</span></div>
<div style="text-indent: 21pt;">5.<span style="font-size: 9pt;">2.1</span><span style="font-size: 9pt;">静态</span><span style="font-size: 9pt;">mock object</span></div>
<div style="margin: 0cm 0cm 0pt 21pt; text-indent: 21pt;"><span style="font-size: 9pt;">编写模拟类，并用模拟类创建的对象来代替协作对象与待测试类的对象进行交互，需要编写模拟类。</span></div>
<div style="text-indent: 21pt;">5.<span style="font-size: 9pt;">2.2</span><span style="font-size: 9pt;">动态</span><span style="font-size: 9pt;">mock object</span></div>
<div style="margin: 0cm 0cm 0pt 21pt; text-indent: 21pt;"><span style="font-size: 9pt;">采用运行期动态的生产模拟对象来与待测试类的对象进行交互。由于</span><span style="font-size: 9pt;">mock object</span><span style="font-size: 9pt;">是动态生成的，因此不需要编写模拟类。目前已经有很多这样的动态</span><span style="font-size: 9pt;">mock object</span><span style="font-size: 9pt;">的框架，如</span><span style="font-size: 9pt;">JMock</span><span style="font-size: 9pt;">，</span><span style="font-size: 9pt;">EasyMock</span><span style="font-size: 9pt;">。不论你采用哪一种动态</span><span style="font-size: 9pt;">mock object</span><span style="font-size: 9pt;">框架，一般都遵循以下的步骤：</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">(1).</span><span style="font-size: 9pt;">创建</span><span style="font-size: 9pt;">mock</span><span style="font-size: 9pt;">实例。</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">(2).</span><span style="font-size: 9pt;">记录对</span><span style="font-size: 9pt;">mock</span><span style="font-size: 9pt;">调用的期望</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">(3).</span><span style="font-size: 9pt;">重放</span><span style="font-size: 9pt;">(replay)</span><span style="font-size: 9pt;">状态。</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">(4).</span><span style="font-size: 9pt;">把</span><span style="font-size: 9pt;">mock</span><span style="font-size: 9pt;">实例作为交互对象来参加测试</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">(5).</span><span style="font-size: 9pt;">调用使用</span><span style="font-size: 9pt;">mock</span><span style="font-size: 9pt;">的测试用例</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">(6).</span><span style="font-size: 9pt;">验证</span><span style="font-size: 9pt;">mock</span><span style="font-size: 9pt;">行为</span></div>
<div style="margin: 13pt 0cm;"><strong><font size="1"><a name="_Toc153600108">5.3</a><span>用Cactus</span><span>进行容器内测试</span></font></strong></div>
<div style="margin: 0cm 0cm 0pt 18pt; text-indent: 21pt;"><span style="font-size: 9pt;">采用</span><span style="font-size: 9pt;">stub</span><span style="font-size: 9pt;">或者</span><span style="font-size: 9pt;">mock object</span><span style="font-size: 9pt;">编写的</span><span style="font-size: 9pt;">TestCase</span><span style="font-size: 9pt;">（</span><span style="font-size: 9pt;">Plain JUnit TestCase</span><span style="font-size: 9pt;">）</span><span style="font-size: 9pt;">,</span><span style="font-size: 9pt;">只能验证单个类的行为，而不能验证真个系统的各个组件之间的交互，要验证这样的交互是否正确，比如一个请求是否正确的转发给相应的组件？一个控制器是否正确的访问某个业务对象？一个</span><span style="font-size: 9pt;">DAO</span><span style="font-size: 9pt;">是否正常的访问了数据库？你的数据连接是否能正常连接到目标数据库呢？这些是</span><span style="font-size: 9pt;">Plain Junit TestCase</span><span style="font-size: 9pt;">没法测试的，对于这样的代码，我们需要编写集成单元测试，借助</span><span style="font-size: 9pt;">Apache</span><span style="font-size: 9pt;">的</span><span style="font-size: 9pt;">Cactus</span><span style="font-size: 9pt;">可以很轻松的编写这样的</span><span style="font-size: 9pt;">TestCase</span><span style="font-size: 9pt;">。</span></div>
<div style="margin: 17pt 0cm 16.5pt;"><strong><font size="3"><a name="_Toc153600109">6 </a><span>测试覆盖率</span></font></strong></div>
<div><span style="font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt;">测试</span><span style="font-size: 9pt;">覆盖率</span><span style="font-size: 9pt;">对单元测试具有重要的意义，但是不要误用。测试</span><span style="font-size: 9pt;">覆盖</span><span style="font-size: 9pt;">报告最好用来检查哪些代码没有充分的测试。当您检查覆盖报告时，找出较低的值，并了解为什么特定的代码没有经过充分的测试。然后采取相应的解决方法。测试</span><span style="font-size: 9pt;">覆盖</span><span style="font-size: 9pt;">报告一般还可以用来：</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">1.</span><span style="font-size: 9pt;">估计修改已有代码所需的时间</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">2.</span><span style="font-size: 9pt;">评估代码质量</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">3.</span><span style="font-size: 9pt;">评定功能测试</span></div>
<div><span style="font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt;">切记</span><span style="font-size: 9pt;">:</span><span style="font-size: 9pt;">不要为了覆盖率而覆盖，应该为了确定代码的功能编写测试，而不是为了提高覆盖率而编写测试。</span></div>
<div style="text-indent: 21pt;"><span style="font-size: 9pt;">以下是一个真实项目的</span><span style="font-size: 9pt;">2006-8-24</span><span style="font-size: 9pt;">日的包</span><strong><span style="font-size: 9pt;">com.numen.ralos.web.action</span></strong><span style="font-size: 9pt;">测试覆盖报告：</span></div>
<div align="center" style="text-indent: 21pt;"><span style="font-size: 9pt;">图</span><span style="font-size: 9pt;">1.</span><span style="font-size: 9pt;">测试覆盖报告</span></div>
<div style="margin: 17pt 0cm 16.5pt;"><strong><font size="3"><a name="_Toc153600110">7.</a><span>最佳实践</span></font></strong></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">1.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">测试用例应当经济，同时又保持自描述性</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">2.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">必要时对测试套件进行重构</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">3.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">避免编写有副作用的测试</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">4.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">连续的回归测试：测试系统自动化</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">5.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">一次只测试一个对象</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">6.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">测试要尽可能地小，执行速度快</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">7.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">反向测试</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">8.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">等价划分</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">9.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">测试自动化</span></div>
<div style="margin: 17pt 0cm 16.5pt;"><strong><font size="3"><a name="_Toc153600111">8.Spring</a>spring<span>的经验</span></font></strong></div>
<div style="margin: 0cm 0cm 0pt 36pt;"><span style="font-size: 9pt;">&nbsp;</span><span style="font-size: 9pt;">这部分内容取自</span><span style="font-size: 9pt;">Expert One-on-One&trade;J2EE&trade; Development without EJB&trade;[5]</span><span style="font-size: 9pt;">。</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">1.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">小步前进。一次只实现一部分功能。</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">2.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">如果你认为一段待命的测试不够充分，请立即为它加上更多的测试。</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-indent: -3pt;"><span style="font-size: 9pt;">3.<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 9pt;">测试的名字要有意义，显示除它们的用途。</span></div>
<div style="margin: 0cm 0cm 0pt 39pt; text-in