<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>Tin</title>
    <description>我是我老婆的好丈夫。</description>
    <link>http://tin.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>银行支付平台开着firebug还是不好用</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/136249" style="color:red;">http://tin.javaeye.com/blog/136249</a>&nbsp;
          发表时间: 2007年10月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          喜欢用firefox的朋友，在使用网上支付的时候经常发现页面不好用。那么我们用firebug，马上debug一下。<br />
然后我们手工修改数据，提交表单，庆幸自己会javascript&hellip;&hellip;<br />
<br />
但是，到了支付的页面，比如招行&hellip;&hellip;我们发现firefox没有用插件，没法输入用户名密码。没法子，继续打开ie。<br />
这也解释了用Ubuntu也要开个ie4linux&hellip;&hellip;<br />
<br />
烦人呀。<br />
<br />
所以徐继哲同志才会去搞告招商银行的活动。此时我还是声援的。只是我不想把这个事情和自由软件绑定，毕竟我还不想传染或者软强制的方式实现自由。
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/136249#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/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 29 Oct 2007 10:30:08 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/136249</link>
        <guid>http://tin.javaeye.com/blog/136249</guid>
      </item>
      <item>
        <title>Java交互管理工具——SecureJSH发布</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/106965" style="color:red;">http://tin.javaeye.com/blog/106965</a>&nbsp;
          发表时间: 2007年07月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://www.infoq.com/cn/news/2007/07/java-securejsh-publish">本文已经发表于InfoQ</a><br />
<br />
<p>JavaEye上活跃的<a href="http://complystill.javaeye.com/">开发者Complystill（歆渊）</a>最近发布了自己的开源<a href="../../../topic/100212">SecureJSH项目</a>，提供了一个通过SSH交互进行Java应用开发或者管理的工具。</p>
<p>在项目的介绍中可以得知，SecureJSH与Ptyhon里面的ipython或者Ruby里面的irb非常相似。它们都允许交互式运行语言的代 码，以方便跟踪或者调试应用。但是，Java与Ruby、Python不同，后者是动态脚本语言，它们天生具有解释执行的特点（注意：当然Python支 持预编译，Ruby也将在YARV中开始支持，这里指它们的解释执行状态）。我们常见的Python和Ruby发行版本基本上都包括自己的解释器（这也是 它们的核心组件），但是Java是一种需要中间编译过程的语言，默认情况下它无法直接解释运行，也没有相应的解释器。</p>
<p>那么SecureJSH是如何实现的呢？读者首先会想到JSR-223，这个API可以自己扩展脚本语言支持，比如rhino是 Javascript解释引擎。但是使用它难以实现交互操作，因为它必须输入一个相对完整的脚本才可以运行，这样会丧失一部分交互性。SecureJSH 实际上是使用了JDK 6.0的新特性Java Compiler API（JSR-199），它提供了一组API来让程序可以动态地访问Java编译器的接口，这样就可以使用Java编译器动态检查代码语法或者动态根据 Java源码生成可以执行的字节码。这种方式与ASM的编程直接生成字节码不同，它能直接将Java源码转换为字节码，XRuby的主力开发者郑晔（网名 dreamhead）在他的Blog中<a href="http://www.infoq.com/cn/news/2007/07/java-securejsh-publish" logs="" dreamhead.blogbus.com="">这样对比了两种方案</a>：</p>
<blockquote style="background-color: rgb(192, 192, 192);">之前，刚刚<a href="http://dreamhead.blogbus.com/logs/2006/12/4007513.html">在Blog中提到ASM</a>， 里面的代码生成工作是通过直接写 字节码完成的。现在有了Compiler API，可以考虑生成代码以Java源码的形式完成，然后，通过调用Compiler API对源码进行动态编译，这样，可以达到同直接写字节码类似的作用。使用Compiler API，肯定不如直接生成字节码来得高效，但对于不了解JVM指令的人来说这也许是一种解决方案。</blockquote>
<p>可见JSR-199不是最高效的字节码生成方案，但是更方便使用。Java Compiler API不是为了取代ASM这样的方案的，它的本意是以编程的方式实现实时编译及信息反馈。Java目前的主要架构师之一Peter von der Ah&eacute;曾经在他的Blog对谁需要使用Java Compiler API这个问题做了<a href="http://blogs.sun.com/ahe/entry/java_compiler_api_who">如下解释</a>：</p>
<blockquote style="background-color: rgb(192, 192, 192);">99%的Java开发者都不需要了解Java Compiler API。只有少数的开发者会直接应用这个API。但是IDE、Java EE应用程序服务器、Maven或者Ant还有测试框架的开发者却不一样，他们有一个共同点，就死需要调用编译器将Java源码转换为类文件（他们是这个 API的潜在用户）。</blockquote>
<p>可见JSR-199的产生主要是面向热部署或者增量编译这样的场合，但是SecureJSH的产生扩展了Java Compiler API的应用场景，同时也增强了Java和JVM的交互性。Complystill这样介绍了<a href="https://sjsh.dev.java.net/">SecureJSH的应用场景和需求</a>：</p>
<blockquote style="background-color: rgb(192, 192, 192);">SecureJSH允许Java编写的服务器端应用程序为管理员、客户、开发者和客户端服务提供一个安全shell，这里可以交互性地让Java语言逐句运行。SecureJSH需要JDK 6.0或者JRE 6.0加JAVAC（在classpath中）来运行。</blockquote>   <a href="https://sjsh.dev.java.net/">SecureJSH的官方首页</a>这样描述了它的主要特性：
<ul style="background-color: rgb(192, 192, 192);">
    <li><strong>安全：</strong>SecureJSH在服务器端实现了RFC-4251，SSH 2.0协议，支持公钥认证，这种方式方便安全（不需要每次输入密码）。</li>
    <li><strong>交互式执行：</strong>传 统的方式下，在运行Java源代码之前你必须将它们编译为字节码。但是使用SecureJSH，编译的过程是透明完成的，所以你只需要随意输入一些 Java表达式（就可以运行）。这意味着你可以使用你书写应用程序时完全相同的语法，与最新的Java语言规范同步。你可以在你的Java项目源码和 SecureJSH终端里面拷贝＆粘贴任何代码，都没有问题。</li>
    <li><strong>智能命令识别，UNIX Shell风格：</strong>不 像JSR-223（Java Scripting Engin，Java脚本引擎）对Java语言的脚本的支持，在（Java脚本引擎）里面你必须将Java类的全部代码输入后才可以执行， secureJSH更加智能和人性化，如果你输入了不完整的Java表达式，它会自动提示你进行多行的输入，然后将这些表达式包装到一个预先定义的类结构 中来执行。它是一个真正的Shell。</li>
    <li><strong>没有相互干扰，最小化资源消耗：</strong>SecureJSH没有需要储存在JVM范围的静态资源，每一个实例只消耗很少量的资源（基于NIO实现，所有的SSH通讯都由一个线程处理）。你可以按照你的想法在一个JVM里面运行任意多个shell服务，包括Java应用程序服务器的JVM。</li>
</ul>
<p>作为一个开源项目，SecureJSH使用了ganymed的纯Java实现的SSH 2.0库，并使用Java NIO编写了网络服务，代码质量很高。据Comply Still介绍，SecureJSH最初是为内存数据库TOB设计的，为这个面向对象数据库提供交互访问的接口，但是后来作者发现它可以被应用在很多场 合，所以单独开源发布。作为Java开发者，您可以从<a projectdocumentlist="" href="https:///" sjsh.dev.java.net="" servlets="">这里下载源码</a>从中学习SSH 2.0、NIO网络服务、Java Compiler API的使用方法，相信一定会有所收获。</p>
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/106965#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/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 30 Jul 2007 13:47:29 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/106965</link>
        <guid>http://tin.javaeye.com/blog/106965</guid>
      </item>
      <item>
        <title>SecureJSH简介</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/104043" style="color:red;">http://tin.javaeye.com/blog/104043</a>&nbsp;
          发表时间: 2007年07月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>本文系SecureJSH的中文介绍，原文参考<a href="http://complystill.javaeye.com/">歆渊</a>的<a href="http://www.javaeye.com/topic/100212">帖子</a>：</p>
<p>SecureJSH允许Java编写的服务器端应用程序为管理员、客户、开发者和客户端服务提供一个安全shell，这里可以交互性的让Java语言逐句的运行。 SecureJSH需要JDK 6.0或者JRE 6.0加JAVAC（在classpath中）来运行。 </p>
<p>安全： SecureJSH在服务器端实现了RFC-4251，SSH 2.0协议，支持公钥认证，这种方式方便安全（不需要每次输入密码）。 </p>
<p>交互式执行：传统的方式下，在运行Java源代码之前你必须将它们编译为bytecode。但是使用SecureJSH，编译的过程是透明完成的，所以你只需要随意输入一些Java表达式（就可以运行）。这意味着你可以使用你书写应用程序时完全相同的语法，与最新的Java语言规范同步。你可以在你的Java项目源码和SecureJSH终端里面拷贝＆粘贴任何代码，都没有问题。 </p>
<p>智能命令识别，UNIX Shell风格不像JSR-223（Java Scripting Engin，Java脚本引擎）对Java语言的脚本的支持，在（Java脚本引擎）里面你必须将Java类的全部代码输入后才可以执行，SecureJSH更加智能和人性化，如果你输入了不完整的Java表达式它会自动提示你进行多行的输入，然后将这些表达式包装到一个预先定义的类结构中来执行。它是一个真正的Shell。 </p>
<p>没有界面，最小化资源消耗：SecureJSH没有需要储存在JVM范围的静态资源，每一个实例只消耗很少量的资源（基于NIO实现，所有的SSH通讯都由一个线程处理）。你可以按照你的想法在一个JVM里面运行任意多个shell服务，包括Java应用程序服务器的JVM。 </p>
<p>长远规划因：为在当代的JVM里面，类载入还是一个昂贵的操作（消耗持久生成的堆区域），SecureJSH意图实现一个在当前JVM之上运行的JVM来直接（on-the-fly）的运行bytecode，而不需要使用类载入。这样可能会比在主JVM上运行要慢很多，但是对于交互会话来说已经足够了，特别是考虑到这样可以摆脱类载入对主JVM长期运行带来的负面影响。</p>
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/104043#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/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 22 Jul 2007 08:46:00 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/104043</link>
        <guid>http://tin.javaeye.com/blog/104043</guid>
      </item>
      <item>
        <title>为dojo.io.IframeIO添加超时，同时简析Ajax的IO方式</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/82169" style="color:red;">http://tin.javaeye.com/blog/82169</a>&nbsp;
          发表时间: 2007年05月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <h2>1、引子</h2>
引出本次讨论的原因是dojo.io.IframeIO的问题。在一个比较大的Javascript应用中使用了dojo，dojo.io是dojo实现的非常好的一个地方，因为dojo.io用一个通用的接口封装了XmlHttp、Iframe、ScriptSrc这几种主流方式（Facade模式），是处理Ajax应用IO的很好选择。但是项目实际部署以后发现经常出现操作无响应的情况，经过反复排查发现是使用的dojo.io.IframeIO的排队没有超时造成的。<br />
那么我们就要寻找问题再哪里呢？<br />
<h2>2、问题</h2>
首先我们看到了dojo.io的bind方法中支持通用的timeout和timeoutSeconds参数，这两个参数从语意上看是为了给你的IO增加超时机制。当初Ajaxian上有一篇介绍实际部署的Ajax项目需要注意的稳定性问题，其中比较重要的一条就是要给你的通讯排队增加超时，而dojo.io的这种设计正体现了这种观点。那么我们还是首先分析一下为什么要排队和为什么要超时机制。<br />
io通讯分同步和异步两种方式，同步即需要阻塞等待，异步即无阻塞等待（异步回调）。我们在使用XML Http的时候可以选择asynchronous为true和false，大家都知道它们就是是否同步。使用的地方大家都很熟悉了，比如两个function有关，它们的调用需要保证严格顺序，那么就应该同步通讯，前一个完了再调后一个。反之如果几个function之间无关，那么就可以异步通讯，减少用户浏览器的阻塞和等待。<br />
OK，这种模型很清楚和简单。可是，实际上就XMLHttp而言，我们一般尽量不使用asynchronous=false，因为这样浏览器会在传输的时候失去对用户的响应，用户感觉比较差。对于需要严格顺序的通讯我们使用另外一种方式，那就是回调的时候再调用下面一个函数，这样也可以保持严格顺序，形成调用链。这种方式其实就是排队模型 。在dojo中有一个queueBind方法，实际上就是包装过的调用链。有了这些机制就可以满足我们日常的编程模型了，似乎很完美了。<br />
可是，问题在于并非所有io都可以提供排队和非排队的方式进行通讯。比如iframe和ScriptSrc的方式实际上都是不支持非排队的，因为这些通讯本质上都是同步的（或者说假异步），在只有一个Dom元素（iframe或者script元素）进行通讯的时候其实所有的io都必须排队。也就是说可怜的iframe和scirptSrc方式都只有排队&hellip;&hellip;而不能不排队&hellip;&hellip;。这似乎也没什么问题，但是问题来了&hellip;&hellip;<br />
HTTP通讯并非可靠的通讯，它们都是最大努力的传输方式，也就是说丢包是不可避免的，在发生丢包的时候如果恰巧你的通讯进行了排队而此时又没有超时&hellip;&hellip;那么你惨了，就出现了北京早晚场出现的堵车现象，往往仅仅是一辆辆车刮蹭了，后面的车就永久的成为了化石，多么的悲壮呀。<br />
所以，排队的超时就非常重要了！一次通讯一定要有超时时间，这样在阻塞时可以把阻塞消除。<br />
dojo在BrowserIO（XMLHttp）和ScriptSrcIO里面实现了超时机制，但是在IframeIO中没有提供，这可能是个历史遗留。<br />
而引子中所说的问题正来自这个不公平的待遇，iFrame的bind实际上就是queueBind（因为dojo的iFrameIO只用了一个iFrame），而又没有超时机制&hellip;&hellip;，然后应用部署在实际网络条件后就有很多用户遇到了操作无响应的问题。<br />
<h2>3、解决</h2>
找到问题并理清思路离解决就非常近了。<br />
dojo的BrowserIO（XMLHttp）和ScriptSrcIO都使用了一种类似onFlight的方式检查超时。就是纪录通讯开始时间，然后传输时开始轮训检查是否超时。不直接用setTimeout检查通讯超时是因为如果有多个ScriptSrc或者XMLHttp通讯的时候可以防止互相冲突。<br />
我们也可以比较简单的向IframeIO中添加添加这样的功能：<br />
OK，这里我偷懒了&hellip;&hellip;。因为我们的项目只有一个iFrame作io，所以我们没有使用onFlight方式，使用了setTimeout直接检查的方式。大家先笑纳，待简单修正把IframeIO也修改为onFlight的方式（然后提交dojo？）。<br />
如此看有点虎头蛇尾，实际上就是这样，问题很大解决方法很小。我们经常遇到的就是这样的问题。<br />
<h2>4、尾声</h2>
最后其实还是要回到javascript应用中的io问题上来。这里我还要简单写一下我们常用到的io方式，我身边还有很多同事不了解所以然。<br />
XML Http是这几个io中最好的一个，因为它是一个完整的IO实现，有状态回调机制，有abort等处理方法，但是相对来说有几个限制：浏览器支持、文件上传、跨域。<br />
浏览器支持现在倒不算个问题，Ajax应用普遍是IE 5.5+、FF 1.0+、Opera 8+这样子，它们都支持XMLHttp了。文件上传用XMLHttp也比较难解决，虽然可以用客户端切分的方法，但是用的人实际上很少（在这点上技术复杂性超过了iframe很多）。<br />
XMLHttp最大的限制是跨域，很严格，甚至不能跨子域，这给我们的通讯带来了巨大的限制。因为很多时候我们要使用proxy或者负载均衡的分布式来解决这个问题，这就增加了部署复杂性。<br />
相对来说iframe则放松了这样的限制，你可以跨子域（比如www.abc.com和ajax.abc.com和cde.xxx.abc.com），我们可以在文档里面通过domain=&quot;abc.com&quot;来强制用根域做js执行的域。但是这种做法也有麻烦，比如Firefox 1.0.x的一些版本如果domain=&quot;abc.com&quot;执行以后会造成XMLHttp通讯失败，此时我们就没法在用iframe的同时用XMLHttp了。<br />
iframe的另外一种好处是它比较容易解决文件上传的问题，只要对form指向一下target为iframe就可以无刷新上传了，这几乎是最简单的方案。<br />
但是iframe的限制还是满多的，它比较复杂，在Tencent这样的倒霉浏览器中设置了所有链接都在新窗口中打开后会让应用很难看并不可用，它没有完全的解决跨域问题等等。<br />
为了解决iframe的不能跨根域的问题，引入了ScriptSrc的方式。就是指<script src="xxx.js" id="scriptIo"></script>，然后动态修改src这样的方式。为了浏览器兼容，此种方式下需要动态的创建script节点，然后再删除，否则内容不会载入（非IE的情况）。所以技术复杂度相对也比较高。但是这种方式可以完全解决跨域问题，你可以将src指向任何地方！<br />
那么缺点是ScriptSrc在动态删节点的时候潜在存在内存泄露，据称不太好解决。而更大的问题是如果传输json的话，必须用var json = {property:value};这样的形势，对json有了污染，不太好。<br />
所以，对于io的方案主要来说几个点：<br />
a、跨域：ScriptSrc完全跨域，iFrame跨子域，XmlHttp不跨域。<br />
b、文件：传文件iframe比较简单，ScriptSrc和XmlHttp都需要客户端切分get上去，比较麻烦。<br />
c、生命周期控制：XmlHttp比较完善，而iFrame可以模拟但是不完全，ScriptSrc则更难。<br />
<br />
That's all。本来想No fluff just staff的写这篇东西，但是无奈嘴碎写的优点乱，请大家见谅。关于本问题的解决希望大家多提意见，互相交流。
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/82169#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/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 23 May 2007 11:32:26 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/82169</link>
        <guid>http://tin.javaeye.com/blog/82169</guid>
      </item>
      <item>
        <title>[翻译]使用测试分类(test categorization)进行敏捷构建</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/36765" style="color:red;">http://tin.javaeye.com/blog/36765</a>&nbsp;
          发表时间: 2006年12月01日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><font size="2">本文翻译自IBM DeveloperWorks上的一篇文章，该文讲述了测试分类(test categorization)的概念，本身这个概念很简单，但是却实际的解决我们常见的问题，在我们的测试庞大到一定地步的时候，测试的运行时间过长，维护成本很高，我们如何能够保证持续集成(CI)的正常运行？那就是通过测试分类。所以我翻译了这片文章，希望对大家有所帮助。<br />
<br />
原文：</font><a href="http://www-128.ibm.com/developerworks/java/library/j-cq10316/" target="_blank"><font size="2"><span style="COLOR: #999999">In pursuit of code quality: </span><font color="#333300">Use test categorization for agile builds</font></font></a><br />
<font size="2">原文作者：Andrew Glover is president of </font><a href="http://www.stelligent.com/"><font size="2" color="#5c81a7">Stelligent Incorporated</font></a><font size="2">, which helps companies address software quality with effective developer testing strategies and continuous integration techniques that enable teams to monitor code quality early and often. Check out </font><a href="http://www.thediscoblog.com/publications/"><font size="2" color="#5c81a7">Andy's blog</font></a><font size="2"> for a list of his publications.<br />
<br />
大家都同意开发人员的测试很重要，但是为什么要花这么长的时间运行测试呢？这个月，Andrew Glover将给我们讲述对于系统来说需要保证运行的三类测试，并且告诉你如何根据分类整理和运行测试。结果将会奇迹般的减少build的时间，即使是面对当今庞大的测试集。<br />
如果不太难过的话，假想一下你是一个2002年初刚刚建立的公司的开发人员。在淘金热潮中，你和你的同事已经决定使用最流行最强大的Java API来开发一个庞大的数据驱动的Web应用程序。你可你的管理团队坚定的信仰敏捷过程。从第一天开始，就使用JUnit编写测试，并且通过Ant build脚本尽可能频繁的运行它们。最后，你们还会使用cron（*nix下的一个定时运行脚本的任务）来进行nightly build。再然后，某些人可能会下在CruiseControl然后把测试写成套件，然后在每次check-in时执行（持续集成）。<br />
现在回到今天。<br />
经过了前几年的磨练，你的公司已经开发了数量巨大的代码，当然也有同样庞大的JUnit测试。一年前所有的事情都运转良好，当你的测试套件有超过2000个测试，人们开始注意到build过程可能要执行三个小时以上。几个月以前，你停止通过代码提交来处罚持续集成（CI）运行单元测试，因为CI服务器会因此过渡繁忙。你改为进行nightly测试（每日测试），第二天早上开发人员可能会头疼测试为何失败。<br />
最近，测试套件似乎很难在晚上运行一次以上了&mdash;&mdash;这是为什么呢？它们永远运行不完！没有人会用几个小时的时间来等待确认代码运行是正常的（或不正常）。所以，整个的测试会在晚上运行，对么？<br />
因为你如此频繁的运行测试，他们总是充满了问题。（译者注：你会开始怀疑是不是测试也出了问题，是否想测试你的测试？）从而，你和你的团队开始怀疑单元测试的价值：如果代码质量并不那么重要，为什么我们要承受这种痛苦？假如你可以用敏捷的方法运行它们的话，你们完全同意这是单元测试的基本价值。 </font></p>
<p><font size="2">&nbsp;</font></p>
<font size="2"><hr />
</font>
<p><font size="2">&nbsp;</font></p>
<p><font size="2"><strong>尝试测试分类（test categorization）</strong> </font></p>
<p><font size="2">你需要的是一个让你的build转变到更敏捷状态的策略。你需要一种解决方案来允许你在一天内多次运行测试，让那些已经需要三个小时完成的测试回到原先的状态。<br />
在你尝试使用这个策略让你的测试套件恢复原形之前，思考一下&ldquo;单元测试&rdquo;的基本概念可能会有所帮助。&ldquo;我家有一只动物&rdquo;和&ldquo;我喜欢汽车&rdquo;这样的陈述不是非常明确，所以，不幸的是，&ldquo;我们编写单元测试&rdquo;也不明确。现在，但愿测试泛指一切。<br />
思考前面的两个关于动物和汽车的陈述：它们产生了很多疑问。例如，你家里有什么动物？是猫、蜥蜴还是熊？&ldquo;我家有一只熊&rdquo;与&ldquo;我家有一只猫&rdquo;完全不同。同样的，&ldquo;我喜欢汽车&rdquo;对于与汽车销售商交谈时没有帮助。你喜欢哪种车：运动车、卡车或者大货车？不同的答案会将你引入不同的路径。<br />
同样，对于开发人员进行测试，根绝测试类型分类是有所帮助的。这样做更加精确，能够允许你的团队以不同的频度运行不同类型的测试。分类是避免恼人的运行所有&ldquo;单元测试&rdquo;的三小时build的关键方法。<br />
</font></p>
<p><font size="2"><strong>三种分类</strong> </font></p>
<p><font size="2">形象的将你的测试套件整理为三层，每一层代表开发人员进行的不同类型的测试，它们是根据运行时间的长短划分的。如图1所示，每一层将花费更多的总build时间，无论是运行时间还是编写它们所需的时间。 </font></p>
<p><strong><font size="2">图1 测试分类的三层<br />
<img src="http://www-128.ibm.com/developerworks/java/library/j-cq10316/testcats.gif" alt="" /><br />
<br />
</font></strong></p>
<p><font size="2">最下面一层测试运行时间最短，如你所想，他们也是最容易写的。他们也覆盖最少量的代码。顶层是有高层次的测试组成，它们检测应用程序的很大一部分。这些测试相对难写，同时也需要更多时间来执行。中间一层测试介于两个极端之间。<br />
这三个分类如下：<br />
</font></p>
<ul>
    <li><strong><font size="2">单元测试 </font></strong></li>
    <li><strong><font size="2">组件测试 </font></strong></li>
    <li><strong><font size="2">系统测试 </font></strong></li>
</ul>
<font size="2">让我们分别的考察它们。 </font>
<p><font size="2">&nbsp;</font></p>
<p><strong><font size="2">1、单元测试 </font></strong></p>
<p><font size="2">单元测试隔离的确认一个或者多个对象。单元测试不处理数据库、文件系统或者任何可能带来测试不能保证长期可运行的因素；顺序上，测试可以从（项目）第一天就开始写。事实上，这就是JUnit的设计目标。单元测试的隔离概念是在很多mock对象库隔离特定对象的外在依赖的基础上的。进一步说，单元测试可以在实际代码编写前就开始写&mdash;&mdash;也就是测试先行开发TDD的概念。<br />
单元测试一般容易编写，因为他们不依靠于系统依赖，并且他们运行迅速。不好的方面是，单独的单元测试只能提供有限的代码覆盖度。单元测试的价值在于允许开发者在最低的依赖程度下保证对象的质量。<br />
因为单元测试运行迅速容易编写，一个代码库应该有很多单元测试且尽量频繁的运行它们。你应该在每次build的时候运行它们，不管是在你的机器或者一个CI环境（以为这你应该在每次向SCM系统chech in之前运行它们）。 </font></p>
<p><font size="2"><strong>2、组件测试</strong> </font></p>
<p><font size="2">组件测试保证多个对象的交互，但是它们突破了代码隔离的概念。因为组件测试处理多层架构，他们经常要处理数据库、文件系统、网络元素等。而且组件测试一般很难在（项目）前编写，所以将它们加入到一个实际的测试先行/测试驱动的场景中是个很大的挑战。<br />
组件测试编写要花多一些时间，因为他们比单元测试要棘手。从另一个方面来看，他们能够提供比单元测试更高的代码覆盖率因为它们的宽工作范围。它们运行耗时更多，所以它们会极大地拖长你们的总测试耗时。<br />
一个宿主框架可能减少测试庞大架构组建的挑战难度。DbUnit就是一个这种框架的完美例子。DbUnit是编写依赖于数据库的测试容易，它能够处理复杂的数据库状态准备工作。<br />
当测试引起build时间延长，你基本上可以确定那就是大组的组件测试造成的。因为这些测试比单元测试运行时间更长，你可能发现你不能总是运行它们。因此，它让CI环境至少以小时为间隔执行它们。你一应该要求每个开发者在check in前在本机环境运行这些代码。 </font></p>
<p><font size="2"><strong>3、系统测试</strong> </font></p>
<p><font size="2">系统测试从端到端保证软件应用。因此，他们提出了高度的架构复杂性：整个应用必须在进行系统测试时运行。如果是一个Web应用程序，你需要访问数据库，从Web服务器、（应用程序）容器、任何相关的配置都要配合系统测试的运行。系统测试总是在软件开发周期的最后阶段撰写的。<br />
系统测试对于编写人员是个挑战，并且实际往往花费比较长的时间。另一方面，他们提供更好的催款理由，也就是说，他们提供了系统架构级的代码覆盖率。<br />
系统测试与功能测试非常相近。区别在于它们不是一个假扮用户，用户是虚拟的。就像组件测试一样，很多框架都是来帮助这类测试的。例如，jWebUnit通过模拟一个浏览器提供了测试Web应用程序的基础设施。<br />
</font></p>
<p style="BORDER-RIGHT: #666 2px dotted; PADDING-RIGHT: 10px; BORDER-TOP: #666 2px dotted; PADDING-LEFT: 10px; FONT-SIZE: 12px; BACKGROUND: #ccc; PADDING-BOTTOM: 10px; BORDER-LEFT: #666 2px dotted; PADDING-TOP: 10px; BORDER-BOTTOM: #666 2px dotted"><font size="2"><strong>什么是接受测试？</strong> <br />
接受测试与功能测试类似，不同点在于，理想情况下，客户或者最终用户来编写接受测试。与功能测试类似，接受测试按照最终用户的行为测试。一个备受关注的接受测试框架是Selenium，它使用浏览器来测试Web应用程序。Selenium可以在build过程中自动运行，就像JUnit测试一样。但是Selenium是一个新的平台：他不一定使用JUnit，方式也不太一样。（Selenium RC就没有这个问题了）</font></p>
<p style="BORDER-RIGHT: #666 2px dotted; PADDING-RIGHT: 10px; BORDER-TOP: #666 2px dotted; PADDING-LEFT: 10px; FONT-SIZE: 12px; BACKGROUND: #ccc; PADDING-BOTTOM: 10px; BORDER-LEFT: #666 2px dotted; PADDING-TOP: 10px; BORDER-BOTTOM: #666 2px dotted"><font size="2"><strong>我应该使用jWebUnit或者Selenium？</strong> <br />
jWebUnit是一个JUnit扩展框架，设计用来进行系统测试；所以，它需要你自己写这些测试。Selenium是一个优秀的接受测试和功能测试工具，不同于jWebUnit，它允许非程序员编写测试。理想状态下，你的团队可以同时使用两种工具来确认应用程序的功能。</font></p>
<p style="BORDER-RIGHT: #666 2px dotted; PADDING-RIGHT: 10px; BORDER-TOP: #666 2px dotted; PADDING-LEFT: 10px; FONT-SIZE: 12px; BACKGROUND: #ccc; PADDING-BOTTOM: 10px; BORDER-LEFT: #666 2px dotted; PADDING-TOP: 10px; BORDER-BOTTOM: #666 2px dotted"><font size="2"><strong>使用TestNG进行测试分类</strong> <br />
使用TestNG实现测试分类非常容易。使用TestNG的group注释，逻辑上将测试分类就是进行合适的group注释，这非常简单。运行某一分类的测试只需要将group名称传给test runner就可以了，例如通过Ant。</font></p>
<p><font size="2"><strong>实现测试分类</strong> <br />
<br />
所以，你的单元测试套件实际上是单元测试、组件测试和系统测试的套件。甚至，在你检查所有的测试后发现build需要这么长时间是因为大部分测试都是组件测试。下一个问题是，如何通过JUnit实现测试分类？<br />
你有很多选择，但是让我们先试验一下最简单的两个：</font></p>
<li><font size="2">根据需要的分类创建不同的JUnit套件（suite）文件 </font></li>
<li><font size="2">对于不同类型的测试创建不同的目录 </font>
<p><strong><font size="2">创建不同的套件</font></strong></p>
<p><font size="2">你可以使用JUnit的TestSuite类（它也是一种Test）定义一组同类测试的集合。你要创建一个TestSuite的实例并添加相关的测试类到test方法中。你可以在TestSuite实例中通过定义一个叫做suite()的public static方法告诉JUnit这个套件包括哪些测试。所有包括的测试将会一次全部执行。因此你可以通过创建TestSuite来实现测试分类，一个单元测试的TestSuite、一个组件测试的TestSuite，有一个系统测试的TestSuite。<br />
例如清单1的类中的suite()方法创建了一个包含所有组建测试的TestSuite。注意这个类不是非常符合JUnit规范。他既没有继承TestCase，也没有任何测试的定义。但是JUnit会自动发现suite()方法并且运行它返回的所有测试类。</font></p>
<p><strong><font size="2">清单1 单元测试的TestSuite</font></strong></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff"><font size="2">package</font></span><font size="2"><span style="COLOR: #000000">&nbsp;test.org.acme.widget;<br />
<br />
</span><span style="COLOR: #0000ff">import</span></font><font size="2"><span style="COLOR: #000000">&nbsp;junit.framework.Test;<br />
</span><span style="COLOR: #0000ff">import</span></font><font size="2"><span style="COLOR: #000000">&nbsp;junit.framework.TestSuite;<br />
</span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000">&nbsp;test.org.acme.widget.</span><span style="COLOR: #000000">*</span></font><font size="2"><span style="COLOR: #000000">;<br />
<br />
</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">class</span></font><font size="2"><span style="COLOR: #000000">&nbsp;ComponentTestSuite&nbsp;{<br />
<br />
&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span></font><font size="2"><span style="COLOR: #000000">&nbsp;main(String[]&nbsp;args)&nbsp;{<br />
&nbsp;&nbsp;junit.textui.TestRunner.run(ComponentTestSuite.suite());<br />
&nbsp;}<br />
<br />
&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">static</span></font><font size="2"><span style="COLOR: #000000">&nbsp;Test&nbsp;suite(){<br />
&nbsp;&nbsp;TestSuite&nbsp;suite&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span></font><font size="2"><span style="COLOR: #000000">&nbsp;TestSuite();<br />
&nbsp;&nbsp;suite.addTestSuite(DefaultSpringWidgetDAOImplTest.</span><span style="COLOR: #0000ff">class</span></font><font size="2"><span style="COLOR: #000000">);<br />
&nbsp;&nbsp;suite.addTestSuite(WidgetDAOImplLoadTest.</span><span style="COLOR: #0000ff">class</span></font><font size="2"><span style="COLOR: #000000">);<br />
&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif" alt="" /><br />
&nbsp;&nbsp;suite.addTestSuite(WidgetReportTest.</span><span style="COLOR: #0000ff">class</span></font><font size="2"><span style="COLOR: #000000">);<br />
&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span></font><span style="COLOR: #000000"><font size="2">&nbsp;suite;<br />
&nbsp;}<br />
}<br />
</font></span></div>
<p><font size="2">&nbsp;</font></p>
<p><font size="2"><strong>清单2&nbsp; 运行组建测试的一个Ant任务</strong> </font></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><font size="2"><span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">target&nbsp;</span> <span style="COLOR: #ff0000">name</span> <span style="COLOR: #0000ff">=&quot;component-test&quot;</span> </font><font size="2"><span style="COLOR: #ff0000">&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if</span> <span style="COLOR: #0000ff">=&quot;Junit.present&quot;</span> </font><font size="2"><span style="COLOR: #ff0000">&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;depends</span> <span style="COLOR: #0000ff">=&quot;junit-present,compile-tests&quot;</span> <span style="COLOR: #0000ff">&gt;</span> </font><span style="COLOR: #000000"><br />
<font size="2">&nbsp;</font></span><font size="2"> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">mkdir&nbsp;</span> <span style="COLOR: #ff0000">dir</span> <span style="COLOR: #0000ff">=&quot;${testreportdir}&quot;</span> <span style="COLOR: #0000ff">/&gt;</span> </font><font size="2"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;<br />
&nbsp;</span> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">junit&nbsp;</span> <span style="COLOR: #ff0000">dir</span> <span style="COLOR: #0000ff">=&quot;./&quot;</span> <span style="COLOR: #ff0000">&nbsp;failureproperty</span> <span style="COLOR: #0000ff">=&quot;test.failure&quot;</span> </font><font size="2"><span style="COLOR: #ff0000">&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printSummary</span> <span style="COLOR: #0000ff">=&quot;yes&quot;</span> </font><font size="2"><span style="COLOR: #ff0000">&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fork</span> <span style="COLOR: #0000ff">=&quot;true&quot;</span> <span style="COLOR: #ff0000">&nbsp;haltonerror</span> <span style="COLOR: #0000ff">=&quot;true&quot;</span> <span style="COLOR: #0000ff">&gt;</span> </font><span style="COLOR: #000000"><br />
<font size="2">&nbsp;&nbsp;&nbsp;</font></span><font size="2"> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">sysproperty&nbsp;</span> <span style="COLOR: #ff0000">key</span> <span style="COLOR: #0000ff">=&quot;basedir&quot;</span> <span style="COLOR: #ff0000">&nbsp;value</span> <span style="COLOR: #0000ff">=&quot;.&quot;</span> <span style="COLOR: #0000ff">/&gt;</span> </font><font size="2"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">formatter&nbsp;</span> <span style="COLOR: #ff0000">type</span> <span style="COLOR: #0000ff">=&quot;xml&quot;</span> <span style="COLOR: #0000ff">/&gt;</span> </font><font size="2"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">formatter&nbsp;</span> <span style="COLOR: #ff0000">usefile</span> <span style="COLOR: #0000ff">=&quot;false&quot;</span> <span style="COLOR: #ff0000">&nbsp;type</span> <span style="COLOR: #0000ff">=&quot;plain&quot;</span> <span style="COLOR: #0000ff">/&gt;</span> </font><font size="2"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">classpath</span> <span style="COLOR: #0000ff">&gt;</span> </font><span style="COLOR: #000000"><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;</font></span><font size="2"> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">path&nbsp;</span> <span style="COLOR: #ff0000">refid</span> <span style="COLOR: #0000ff">=&quot;build.classpath&quot;</span> <span style="COLOR: #0000ff">/&gt;</span> </font><font size="2"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">pathelement&nbsp;</span> <span style="COLOR: #ff0000">path</span> <span style="COLOR: #0000ff">=&quot;${testclassesdir}&quot;</span> <span style="COLOR: #0000ff">/&gt;</span> </font><font size="2"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">pathelement&nbsp;</span> <span style="COLOR: #ff0000">path</span> <span style="COLOR: #0000ff">=&quot;${classesdir}&quot;</span> <span style="COLOR: #0000ff">/&gt;</span> </font><font size="2"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;</span> </font><span style="COLOR: #0000ff"><font size="2"><span style="COLOR: #800000">classpath</span> <span style="COLOR: #0000ff">&gt;</span> </font><span style="COLOR: #000000"><br />
<font size="2">&nbsp;&nbsp;&nbsp;</font></span><font size="2"> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">batchtest&nbsp;</span> <span style="COLOR: #ff0000">todir</span> <span style="COLOR: #0000ff">=&quot;${testreportdir}&quot;</span> <span style="COLOR: #0000ff">&gt;</span> </font><span style="COLOR: #000000"><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;</font></span><font size="2"> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">fileset&nbsp;</span> <span style="COLOR: #ff0000">dir</span> <span style="COLOR: #0000ff">=&quot;test&quot;</span> <span style="COLOR: #0000ff">&gt;</span> </font><span style="COLOR: #000000"><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span><font size="2"> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">include&nbsp;</span> <span style="COLOR: #ff0000">name</span> <span style="COLOR: #0000ff">=&quot;**/ComponentTestSuite.java&quot;</span> <span style="COLOR: #0000ff">/&gt;</span> </font><font size="2"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span> </font><span style="COLOR: #0000ff"><font size="2"><span style="COLOR: #800000">fileset</span> <span style="COLOR: #0000ff">&gt;</span> </font><span style="COLOR: #000000"><br />
<font size="2">&nbsp;&nbsp;&nbsp;</font></span><font size="2"> </font><span style="COLOR: #0000ff"><font size="2"><span style="COLOR: #800000">batchtest</span> <span style="COLOR: #0000ff">&gt;</span> </font><span style="COLOR: #000000"><br />
<font size="2">&nbsp;</font></span><font size="2"> </font><span style="COLOR: #0000ff"><font size="2"><span style="COLOR: #800000">junit</span> <span style="COLOR: #0000ff">&gt;</span> </font><span style="COLOR: #000000"><br />
</span><span style="COLOR: #0000ff"><font size="2"><span style="COLOR: #800000">target</span> <span style="COLOR: #0000ff">&gt;</span> </font></span></span></span></span></span></div>
<p><font size="2" color="#000000">理想情况下，你还需要一个触发单元测试的任务和系统测试的任务。最后，还有希望运行所有测试的情况，你需要创建第四个任务来运行其它三个任务，就像清单3里面那样： </font></p>
<p><font color="#000000"><font size="2"><strong>清单3 运行所有测试的任务</strong> <br />
</font></font></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><font size="2"><span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">target&nbsp;</span> <span style="COLOR: #ff0000">name</span> <span style="COLOR: #0000ff">=&quot;test-all&quot;</span> <span style="COLOR: #ff0000">&nbsp;depends</span> <span style="COLOR: #0000ff">=&quot;unit-test,component-test,system-test&quot;</span> <span style="COLOR: #0000ff">/&gt;</span> </font></div>
<p><font size="2" color="#000000">创建单独的TestSuite是一个迅速实现测试分类的解决方案。缺点是这个方法需要你创建新的测试，你必须编成式的将它们添加到合适的TestSuite里面，这可能有点痛苦。给每个测试类型创建单独的目录可能是一种更加有弹性的方法，它允许你添加新的测试分类但无需重新编译。<br />
</font></p>
<p><font size="2">&nbsp;</font></p>
<font size="2"><hr />
</font>
<p><font size="2">&nbsp;</font></p>
<p><font color="#000000"><font size="2"><strong>创建单独的目录</strong> </font></font></p>
<p><font size="2" color="#000000">我发现最简单的通过JUnit实现测试分类的方法是逻辑上将不同类型的测试放到不同的目录中。使用这个方法，所有的单元测试都放在unit目录，所有的组建测试都放在component目录，等等。<br />
例如，在test目录中保存着所有未分类的测试，你可以创建三个新的子目录，就像清单4中那样： </font></p>
<p><font size="2"><strong><font color="#000000">清单4 实现测试分类的目录结构</font></strong> <br />
</font></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000"><font size="2">acme-proj/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unit/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;component/<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;system/&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;conf/<br />
</font></span></div>
<p><font size="2" color="#000000">运行这些测试，你需要定义至少四个Ant任务：一个给单元测试，另外的给组建测试，还有系统测试。第四个任务是一个方便运行其它三个测试类型的任务（就像清单3种展示的那种方式）。<br />
</font><font size="2" color="#000000">JUnit任务就像清单2中的形式。区别在哪里呢，只是在任务的batchtest这个地方。这次，fileset指向的是一个指定的目录，就像清单5种的样子，他指向了unit目录：<br />
<br />
<strong>清单5 JUnit任务中的batchtest方面，用来运行所有单元测试</strong> </font></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><font size="2"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top" alt="" /> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">batchtest&nbsp;</span> <span style="COLOR: #ff0000">todir</span> <span style="COLOR: #0000ff">=&quot;${testreportdir}&quot;</span> <span style="COLOR: #0000ff">&gt;</span> </font><span style="COLOR: #000000"><br />
<font size="2"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;</font></span><font size="2"> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">fileset&nbsp;</span> <span style="COLOR: #ff0000">dir</span> <span style="COLOR: #0000ff">=&quot;test/unit&quot;</span> <span style="COLOR: #0000ff">&gt;</span> </font><font size="2"><span style="COLOR: #000000">&nbsp;<br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;</span> <span style="COLOR: #0000ff">&lt;</span> <span style="COLOR: #800000">include&nbsp;</span> <span style="COLOR: #ff0000">name</span> <span style="COLOR: #0000ff">=&quot;**/**Test.java&quot;</span> <span style="COLOR: #0000ff">/&gt;</span> </font><font size="2"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;</span> </font><span style="COLOR: #0000ff"><font size="2"><span style="COLOR: #800000">fileset</span> <span style="COLOR: #0000ff">&gt;</span> </font><span style="COLOR: #000000"><br />
<font size="2"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align="top" alt="" /> </font></span><span style="COLOR: #0000ff"><font size="2"><span style="COLOR: #800000">batchtest</span> <span style="COLOR: #0000ff">&gt;</span> </font></span></span></div>
<p><font size="2" color="#000000">注意这个任务运行test/unit目录下的所有测试，当创建了新的单元测试（或者其它分类的其它测试），你只需要把它们放到这个目录里面就可以了！这比添加一行到TestSuite中并重新编译它要方便多了。<br />
</font></p>
<p><font size="2" color="#000000">&nbsp;</font></p>
<p><font color="#000000"><font size="2"><strong>问题解决了！</strong> </font></font></p>
<p><font size="2" color="#000000">回到最初的场景，我认为你和你的团队会决定使用单独的目录这种弹性的解决方案来解决你们的build时间过长的问题。这个任务最难的一个方面是检查和分清测试类型。你重构你的Ant build文件创建四个新的任务（三个单独的测试分类还有一个运行它们三个）。甚至，你修改CuiresControl只在check-in的时候运行单元测试，而组建测试按小时运行。更进一步的检查后，系统测试也可以几个小时运行一次，也许你会创建一个新的任务来同时运行组建测试和系统测试。 </font></p>
<p><font size="2" color="#000000">最后的结果是每天测试运行很多次，你的团队可以快速的发现集成错误&mdash;&mdash;一般在几个小时内。 </font></p>
<p><font size="2" color="#000000">创建敏捷构建不是为了赶时髦，它实际上是保证代码质量的重要因素。测试运行的更加频繁，开发人员的测试的价值就能直接转化为钱。并且，希望你们的公司能够在2006取得广泛的成功！ </font></p>
<p><strong><font size="2" color="#000000">资源<br />
</font><font size="2" color="#000000">Learn<br />
</font></strong></p>
<ul>
    <li><font size="2">&quot; </font><a href="http://www.ibm.com/developerworks/web/library/wa-selenium-ajax/"><font size="2"><font color="#5c81a7">Automate acceptance tests with Selenium</font> </font></a><font size="2">&quot; (Christian Hellsten, developerWorks, December 2005): Architects, developers, and testers learn how to use the Selenium testing tools to automate acceptance tests. </font></li>
    <li><font size="2">&quot; </font><a href="http://www.onjava.com/pub/a/onjava/2004/01/21/dbunit.html"><font size="2"><font color="#5c81a7">Effective Unit Testing with DbUnit</font> </font></a><font size="2">&quot; (Andrew Glover, OnJava, January 2004): Introduces database-dependent testing with DbUnit. </font></li>
    <li><font size="2">&quot; </font><a href="http://www.ibm.com/developerworks/java/library/j-junit4.html"><font size="2"><font color="#5c81a7">An early look at JUnit 4</font> </font></a><font size="2">&quot; (Elliotte Harold, developerWorks, September 2005): Obsessive code tester Elliotte Harold takes JUnit 4 out for a spin. </font></li>
    <li><font size="2">&quot; </font><a href="http://www.ibm.com/developerworks/java/library/j-cq09266.html"><font size="2"><font color="#5c81a7">Repeatable system tests</font> </font></a><font size="2">&quot; (Andrew Glover, developerWorks, September 2006): Andrew Glover introduces Cargo, an open source framework that automates container management in a generic fashion. </font></li>
    <li><font size="2">&quot; </font><a href="http://www.ibm.com/developerworks/java/library/j-jwebunit/"><font size="2"><font color="#5c81a7">Create test cases for Web applications</font> </font></a><font size="2">&quot; (Amit Tuli, developerWorks, May 2005): Software engineer Amit Tuli introduces jWebUnit. </font></li>
    <li><font size="2">&quot; </font><a href="http://www.ibm.com/developerworks/java/library/j-cq08296/"><font color="#5c81a7"><font size="2"><em>In pursuit of code quality</em>: JUnit 4 vs. TestNG </font></font></a><font size="2">&quot; (Andrew Glover, developerWorks, April 2006): Has JUnit 4 rendered TestNG obsolete? Find out why not. </font></li>
    <li><a href="http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=code+quality:"><font color="#996699"><font size="2"><em>In pursuit of code quality</em> series </font></font></a><font size="2">(Andrew Glover, developerWorks): Learn more about code metrics, test frameworks, and writing quality-focused code. </font></li>
    <li><a href="http://www.ibm.com/developerworks/java/"><font size="2"><font color="#5c81a7">developerWorks</font> </font></a><font size="2">: Hundreds of articles about every aspect of Java programming. </font></li>
</ul>
<p><font color="#000000"><font size="2"><strong>Get products and technologies</strong> <br />
</font></font></p>
<ul>
    <li><a href="http://www.junit.org/"><font size="2"><font color="#5c81a7">Download JUnit</font> </font></a><font size="2">: Find out what's new with JUnit 4. </font></li>
    <li><a href="http://www.testng.org/"><font size="2"><font color="#5c81a7">Download TestNG</font> </font></a><font size="2">: Another powerful testing framework. </font></li>
</ul>
<p><font size="2"><font color="#000000"><strong>Discuss</strong> </font><br />
</font></p>
<ul>
    <li><a href="http://www.ibm.com/developerworks/forums/dw_forum.jsp?forum=812&amp;cat=10"><font size="2"><font color="#5c81a7">Participate in the discussion forum</font> </font></a><font size="2">. </font></li>
    <li><a href="http://www.ibm.com/developerworks/forums/dw_forum.jsp?forum=812&amp;cat=10"><font size="2"><font color="#5c81a7">Discussion forum: Improve your code quality</font> </font></a><font size="2">: Andrew shares his expertise as a consultant focused on improving code quality. </font></li>
</ul>
<p><font size="2">&nbsp;</font></p>
<font size="2">定义TestSuite的过程需要你察看你当前的所有测试并将它们加入到相应的类里面（例如，所有的单元测试加入到UnitTestSuite）。这也就意味着你在相应的分类里面创建了新的测试，你必须编程式的将它们添加到合适的TestSuite中，当然还需要重新编译它们。<br />
运行单独的TestSuite需要单独的Ant任务来运行正确的测试组。你可以定义一个component-test任务来执行ComponentTtestSuite，就像清单2中的样子：</font></li>
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/36765#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></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 01 Dec 2006 00:38:33 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/36765</link>
        <guid>http://tin.javaeye.com/blog/36765</guid>
      </item>
      <item>
        <title>在JUnit中多个testCase只执行一次setup和tearDown的方法</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/35114" style="color:red;">http://tin.javaeye.com/blog/35114</a>&nbsp;
          发表时间: 2006年11月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          这个问题出现在这种情况，你的每个testCase都需要使用某一种初始化比较耗时的对象（资源），举例如数据库连接、Spring Context。我们遇到的问题是Selenium测试中开启和关闭浏览器，如果一个test启动关闭（我们的程序还需要登录和注销），这样测试的时间会拖的很长，给持续集成带来了困难。<br />所以，我们需要在每组不会冲突的test中间共享一个浏览器窗口，这样也就需要一个全局的setUp和tearDown。问题是JUnit 3.8.1里面的setUp和tearDown是在每个test之前和之后运行的，如果在里面初始化和关闭浏览器就会造成上面所说的问题。要解决它，就产生了如下3种思路：<br />1、升级，使用JUnit4<br />JUnit4从TestNG里面吸取了两个注释：@BeforeClass和@AfterClass<br />用它们注释过的方法就会只初始化一次，完全符合我们的需求。<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="/Images/OutliningIndicators/ExpandedBlockStart.gif" id="Codehighlighter1_56_678_Open_Image" onclick="this.style.display='none'; Codehighlighter1_56_678_Open_Text.style.display='none'; Codehighlighter1_56_678_Closed_Image.style.display='inline'; Codehighlighter1_56_678_Closed_Text.style.display='inline';" align="top" /><img src="/Images/OutliningIndicators/ContractedBlock.gif" id="Codehighlighter1_56_678_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_56_678_Closed_Text.style.display='none'; Codehighlighter1_56_678_Open_Image.style.display='inline'; Codehighlighter1_56_678_Open_Text.style.display='inline';" align="top" style="DISPLAY: none" /><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> SeleniumTestCase </span><span style="COLOR: #0000ff">extends</span><span style="COLOR: #000000"> SeleneseTestCase4 </span><span id="Codehighlighter1_56_678_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="/Images/dot.gif" /></span><span id="Codehighlighter1_56_678_Open_Text"><span style="COLOR: #000000">{<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">final</span><span style="COLOR: #000000"> Log log </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> LogFactory.getLog(SeleniumTestCase.</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">);<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> Selenium selenium </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">;<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="/Images/OutliningIndicators/ExpandedSubBlockStart.gif" id="Codehighlighter1_191_267_Open_Image" onclick="this.style.display='none'; Codehighlighter1_191_267_Open_Text.style.display='none'; Codehighlighter1_191_267_Closed_Image.style.display='inline'; Codehighlighter1_191_267_Closed_Text.style.display='inline';" align="top" /><img src="/Images/OutliningIndicators/ContractedSubBlock.gif" id="Codehighlighter1_191_267_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_191_267_Closed_Text.style.display='none'; Codehighlighter1_191_267_Open_Image.style.display='inline'; Codehighlighter1_191_267_Open_Text.style.display='inline';" align="top" style="DISPLAY: none" />    </span><span id="Codehighlighter1_191_267_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/** */</span><span id="Codehighlighter1_191_267_Open_Text"><span style="COLOR: #008000">/**</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />     * 包含了登录的代码，保证在一个测试内部只执行一次开启浏览器并登录操作<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />     * </span><span style="COLOR: #808080">@throws</span><span style="COLOR: #008000"> Exception<br /><img src="/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />     </span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />    @BeforeClass<br /><img src="/Images/OutliningIndicators/ExpandedSubBlockStart.gif" id="Codehighlighter1_342_461_Open_Image" onclick="this.style.display='none'; Codehighlighter1_342_461_Open_Text.style.display='none'; Codehighlighter1_342_461_Closed_Image.style.display='inline'; Codehighlighter1_342_461_Closed_Text.style.display='inline';" align="top" /><img src="/Images/OutliningIndicators/ContractedSubBlock.gif" id="Codehighlighter1_342_461_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_342_461_Closed_Text.style.display='none'; Codehighlighter1_342_461_Open_Image.style.display='inline'; Codehighlighter1_342_461_Open_Text.style.display='inline';" align="top" style="DISPLAY: none" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> startSelenium() </span><span style="COLOR: #0000ff">throws</span><span style="COLOR: #000000"> Exception </span><span id="Codehighlighter1_342_461_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="/Images/dot.gif" /></span><span id="Codehighlighter1_342_461_Open_Text"><span style="COLOR: #000000">{<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />        log.debug(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Starting Selenium<img src="/Images/dot.gif" /></span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />        selenium </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> SeleniumSession.getCurrentSession().getSelenium();<br /><img src="/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" /><br /><img src="/Images/OutliningIndicators/ExpandedSubBlockStart.gif" id="Codehighlighter1_468_530_Open_Image" onclick="this.style.display='none'; Codehighlighter1_468_530_Open_Text.style.display='none'; Codehighlighter1_468_530_Closed_Image.style.display='inline'; Codehighlighter1_468_530_Closed_Text.style.display='inline';" align="top" /><img src="/Images/OutliningIndicators/ContractedSubBlock.gif" id="Codehighlighter1_468_530_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_468_530_Closed_Text.style.display='none'; Codehighlighter1_468_530_Open_Image.style.display='inline'; Codehighlighter1_468_530_Open_Text.style.display='inline';" align="top" style="DISPLAY: none" />    </span><span id="Codehighlighter1_468_530_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/** */</span><span id="Codehighlighter1_468_530_Open_Text"><span style="COLOR: #008000">/**</span><span style="COLOR: #008000"><br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />     * 在该类包含的所有测试结束之后关闭浏览器<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />     * </span><span style="COLOR: #808080">@throws</span><span style="COLOR: #008000"> Exception<br /><img src="/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />     </span><span style="COLOR: #008000">*/</span></span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />    @AfterClass<br /><img src="/Images/OutliningIndicators/ExpandedSubBlockStart.gif" id="Codehighlighter1_603_676_Open_Image" onclick="this.style.display='none'; Codehighlighter1_603_676_Open_Text.style.display='none'; Codehighlighter1_603_676_Closed_Image.style.display='inline'; Codehighlighter1_603_676_Closed_Text.style.display='inline';" align="top" /><img src="/Images/OutliningIndicators/ContractedSubBlock.gif" id="Codehighlighter1_603_676_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_603_676_Closed_Text.style.display='none'; Codehighlighter1_603_676_Open_Image.style.display='inline'; Codehighlighter1_603_676_Open_Text.style.display='inline';" align="top" style="DISPLAY: none" />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> stopSelenium() </span><span style="COLOR: #0000ff">throws</span><span style="COLOR: #000000"> Exception </span><span id="Codehighlighter1_603_676_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="/Images/dot.gif" /></span><span id="Codehighlighter1_603_676_Open_Text"><span style="COLOR: #000000">{<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />        log.debug(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Stoping Selenium<img src="/Images/dot.gif" /></span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="/Images/OutliningIndicators/InBlock.gif" align="top" />        selenium.stop();<br /><img src="/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span></span><span style="COLOR: #000000"><br /><img src="/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span></span></div>这个里面的selenium = SeleniumSession.getCurrentSession().getSelenium();其实是个singleton，第一次open new，后来就直接返回selenium的instance（具体参考其它文章）。<br />这样做非常舒服，因为完全不是Trick，而是新的feature，用起来踏实。这样，这个类的所有@Test就会公用一个selenium打开的浏览器了。<br />那么缺点是什么呢？缺点是放到CI环境的时候如果使用我们习惯的Ant写执行脚本的话必须将Ant升级到1.7Beta3，因为Ant 1.6.5的Junit task不支持JUnit4……当然升级并不会带来代码的变化，但是问题在于Ant 1.7还是Beta，而且JUnit4需要JDK5的Annotation，你的PM估计要撇嘴了<img src="/Emoticons/74_74.gif" border="0" height="19" width="19" /><br /><br />2、JVM级别钩子法<br />因为JVM支持关闭时执行制定代码的钩子，而static代码会在类初始化时执行，再加上Ant调用的是类似命令行的java命令，实际上每一个测试运行在一个完整的JVM启动关闭周期里面，所以也就产生了这种解决方案。<br />这个方案来自<a href="http://wiki.javascud.org/display/SEL/selenium+resources+selenium+tips+2+taowen" target="_blank">taowen同学的两则Selenium经验</a>。<br />代码我恢复了一下，大概是这样：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">abstract</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> SomeTestCase </span><span style="COLOR: #0000ff">extends</span><span style="COLOR: #000000"> TestCase {<br /><br />  </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> {<br />    </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> perform the "global" set up logic<br />    </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">这里的代码会在类初始化时执行，所以相当于BeforeClass</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">    log.debug(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Starting Selenium<img src="/Images/dot.gif" /></span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />        selenium </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> SeleniumSession.getCurrentSession().getSelenium();<br /><br />    </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> and now register the shutdown hook for tear down logic<br />    </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">将一个匿名方法写到这里，就相当于AfterClass</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">    Runtime.getRuntime().addShutdownHook(<br />       </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> Thread(){<br />           </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> run() {<br />             log.debug(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Stoping Selenium<img src="/Images/dot.gif" /></span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />             selenium.stop();<br />           }<br />       }<br />     );<br />  }<br /><br />}</span></div><br />这个方法挺酷的，我认为完全可以被称作“奇技淫巧”。缺点就是，有点不好看。<br /><br />3、还有别的方法，这个来自Selenium网站，似乎是不错的中庸方案。<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> junit.framework.</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">;<br /></span><span style="COLOR: #0000ff">import</span><span style="COLOR: #000000"> junit.extensions.TestSetup;<br /><br /></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> AllTestsOneTimeSetup {<br /><br />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> Test suite() {<br /><br />        TestSuite suite </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> TestSuite();<br /><br />        suite.addTest(SomeTest.suite());<br />        suite.addTest(AnotherTest.suite());<br /><br />        TestSetup wrapper </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> TestSetup(suite) {<br /><br />            </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> setUp() {<br />                oneTimeSetUp();<br />            }<br /><br />            </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> tearDown() {<br />                oneTimeTearDown();<br />            }<br />        };<br /><br />        </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> wrapper;<br />    }<br /><br />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> oneTimeSetUp() {<br />        </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> one-time initialization code</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">    }<br /><br />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> oneTimeTearDown() {<br />        </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> one-time cleanup code</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">    }<br />}<br /><br /></span></div><br /><p>这个好像是比较正统的方案，不好意思我并没有试验，但是看起来这的确可能是限定用JDK 1.4或JUnit 3.8.1的最佳解决方案。欢迎尝试。相关的连接参考这里：<a href="http://www.cs.wm.edu/~noonan/junit/doc/faq/faq.htm#organize_3">http://www.cs.wm.edu/~noonan/junit/doc/faq/faq.htm#organize_3</a> </p>
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/35114#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></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 17 Nov 2006 17:29:00 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/35114</link>
        <guid>http://tin.javaeye.com/blog/35114</guid>
      </item>
      <item>
        <title>Selenium Remote Control 0.9.0发布</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/35115" style="color:red;">http://tin.javaeye.com/blog/35115</a>&nbsp;
          发表时间: 2006年11月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Selenium Remote Control项目组很高兴的宣布Selenium Remote Control 0.9.0发布。<br />你可以在这里察看：<br /><a href="http://www.openqa.org/selenium-rc/">http://www.openqa.org/selenium-rc/</a><br />也可以在这里下载：<br /><a href="http://www.openqa.org/selenium-rc/download.action">http://www.openqa.org/selenium-rc/download.action</a><br />0.9.0包括很多酷玩意，包括frame支持，多窗口支持（用来测试那些不能够在子frame中运行的应用），一个Konqueror浏览器launcher，新的cookie管理功能，和Firefox 2.0与IE7的支持。还包括一个试验性的在Selenium代理中直接的SSL支持，一个新的实验性的“代理注入（proxy injection）”模式允许我们通过修改HTTP代理来更好的控制我们测试的应用程序。<br />Have Fun！
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/35115#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/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 15 Nov 2006 15:57:00 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/35115</link>
        <guid>http://tin.javaeye.com/blog/35115</guid>
      </item>
      <item>
        <title>Selenium相关资源中文化动员</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/35116" style="color:red;">http://tin.javaeye.com/blog/35116</a>&nbsp;
          发表时间: 2006年11月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          
		<h1>
				<a name="Home-Selenium%E7%9B%B8%E5%85%B3%E8%B5%84%E6%BA%90%E4%B8%AD%E6%96%87%E5%8C%96">
				</a>Selenium相关资源中文化</h1>
		<h3>
				<a name="Home-1.%E5%8A%A8%E6%9C%BA">
				</a>1. 动机</h3>
		<p>springside项目的江南白衣、cac和徐昊、熊杰都多次推广非常Pragmatic的Selenium进行功能测试。而我们的项目中也开始引入Selenium测试，并结合持续集成搭建了一个测试环境，也尝试通过Selenium进行一些浏览器兼容性的测试。期间感觉到Selenium的强大，同时也发现这个项目还不是很成熟，还需要很多改进。但是，对于已经起步的程序员，我们觉得应该提供一个学习的场所，所以决定依靠javascud提供的服务，进行Selenium相关资源的中文化。<br />已经与Openqa的Wiki的负责人Patrick lightbody进行了沟通，他非常支持我们翻译Selenium文档，我们将一边翻译一边将翻译结果转移到OpenQA，这样就能够方便所有使用中文的Selenium用户了，希望大家共同努力！</p>
		<h3>2. 初步计划</h3>
		<p>我们准备从翻译 <span class="nobr"><a href="http://wiki.openqa.org/dashboard.action" title="Visit page outside Confluence" rel="nofollow">Selenium相关wiki <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a></span> 开始，翻译openqa下面的Selenium、Selenium RemoteControl、Selenium IDE下面的相关文章。顺序应该是偏重SeleniumRemoteControl和Selenium Core部分。<br />而openqa网站下面Selenium和RC的相关教程我们也会选择性的进行翻译。<br />Selenium On Rails如果哪位朋友感兴趣也可以负责（目前还是空的：）。<br /><strong><font color="#006400">欢迎所有对Selenium感兴趣的（翻译的过程就是很好的学习）或者已经使用Selenium的同学积极参加，参加的方式可以是：直接参与翻译，编写原创学习文章并添加到相关资源列表，交流心得并将心得添加到相关资源列表，提供使用的视频片断，推荐好的Selenium文章进入翻译计划，提出你自己的意见。以上方式都非常欢迎，可以发信给我进行沟通（iamtin AT gmail.com）。<br />目前我们使用JavaScud的Wiki进行翻译工作，网址如下：<br /></font></strong><a href="http://wiki.javascud.org/display/SEL/Home"><strong><font color="#006400">http://wiki.javascud.org/display/SEL/Home</font></strong></a><br /><strong><font color="#006400">需要帐号请自行注册，然后将注册后的帐号发给我带为申请或者直接向scud（飞云小侠）申请。</font></strong></p>
		<h3>
				<a name="Home-3.%E5%B7%A5%E4%BD%9C%E7%BA%BF%E8%B7%AF%E5%9B%BE%E5%B7%A5%E4%BD%9C%E7%BA%BF%E8%B7%AF%E5%9B%BE">
				</a>3. <a href="http://wiki.javascud.org/pages/viewpage.action?pageId=5082" title="工作线路图">工作线路图 </a></h3>
		<p>按照官方网站的目录进行整理，首先翻译比较有意义/实用的部分。本页将作为文档的索引，同时也作为任务分配的页面。如果某一页的内容被翻译者认领，请将自己的名字和文档的翻译状态注在索引后面，方便统计。状态为（working, complete, reviewd），认领后为working状态，翻译好则complete，其它人看过并审过以后为reviewd。<br />我们首先翻译Selenium的几个About页面，然后开始翻译相应的Wiki部分，然后再补全Documentation、Reference部分，其余部分最后补齐。<br />现在wiki和about的原文文档已经粘贴过来了，请大家有工夫的时候开始翻译，这部分翻译完成后我们将把阶段成果链接到openqa的translation部分去。</p>
		<ul>
				<li>Selenium Core 
<ul><li><a href="http://wiki.javascud.org/pages/viewpage.action?pageId=5087" title="Selenium - 关于">关于 </a></li><li>News 
</li><li>Documentation 
</li><li>Usage 
</li><li>Reference 
</li><li>FAQ 
</li><li>Demos 
</li><li>Wiki 
<ul><li><a href="http://wiki.javascud.org/pages/viewpage.action?pageId=5115" title="我应该使用哪种Selenium工具？">我应该使用哪种Selenium工具？</a></li><li><a href="http://wiki.javascud.org/display/SEL/Selenium+Core+FAQ" title="Selenium Core FAQ">Selenium Core FAQ</a></li><li><a href="http://wiki.javascud.org/display/SEL/Getting+Started+with+Selenium+Core" title="Getting Started with Selenium Core">Getting Started with Selenium Core</a></li><li><a href="http://wiki.javascud.org/display/SEL/Selenium+Core+Examples" title="Selenium Core Examples">Selenium Core Examples</a></li><li><a href="http://wiki.javascud.org/display/SEL/Help+With+XPath" title="Help With XPath">Help With XPath</a></li><li><a href="http://wiki.javascud.org/display/SEL/References+and+Citations" title="References and Citations">What People are Saying About Selenium</a></li><li><a href="http://wiki.javascud.org/display/SEL/Publications+and+Presentations" title="Publications and Presentations">Publications and Presentations</a></li><li><a href="http://wiki.javascud.org/display/SEL/Translated+Document" title="Translated Document">Translated Document</a><font color="red">new!!</font></li><li><a href="http://wiki.javascud.org/display/SEL/Selenium+Core+API+Documentation+Standard" title="Selenium Core API Documentation Standard">Selenium Core API Documentation Standard</a></li><li><a href="http://wiki.javascud.org/display/SEL/Contributed+User-Extensions" title="Contributed User-Extensions">Contributed User-Extensions</a></li></ul></li></ul></li>
				<li>Selenium IDE 
<ul><li><a href="http://wiki.javascud.org/pages/viewpage.action?pageId=5097" title="Selenium IDE - 关于">关于 </a></li><li>News 
</li><li>Recording a test (video) 
</li><li>Documentation 
</li><li>Wiki 
<ul><li><a href="http://wiki.javascud.org/display/SEL/Recording+a+Test" title="Recording a Test">Recording a Test</a></li><li><a href="http://wiki.javascud.org/display/SEL/FAQ" title="FAQ">FAQ</a></li><li><a href="http://wiki.javascud.org/display/SEL/Contributed+Extensions+and+Formats" title="Contributed Extensions and Formats">Contributed Extensions and Formats</a></li><li><a href="http://wiki.javascud.org/display/SEL/Automating+Selenium+IDE+tests" title="Automating Selenium IDE tests">Automating Selenium IDE tests</a></li><li><a href="http://wiki.javascud.org/display/SEL/Writing+extensions" title="Writing extensions">Writing extensions</a></li><li><a href="http://wiki.javascud.org/display/SEL/Adding+Custom+Format" title="Adding Custom Format">Adding Custom Format</a></li><li><a href="http://wiki.javascud.org/display/SEL/Building+Selenium+IDE" title="Building Selenium IDE">Building Selenium IDE</a></li></ul></li></ul></li>
				<li>Selenium RC 
<ul><li><a href="http://wiki.javascud.org/pages/viewpage.action?pageId=5096" title="Selenium Remote Control - 关于">关于 </a></li><li>News 
</li><li>Documentation 
</li><li>Tutorial 
</li><li>Troubleshooting/FAQ 
<ul><li>Java 
</li><li>.NET 
</li><li>Perl 
</li><li>Python 
</li><li>Ruby 
</li><li>Selenese </li></ul></li><li>Server Command Line Options 
</li><li>Developer's Guide 
</li><li><a href="http://wiki.javascud.org/display/SEL/Selenium+Remote+Control+-+Wiki" title="Selenium Remote Control - Wiki">Wiki</a><ul><li><a href="http://wiki.javascud.org/display/SEL/Possible+Solution+to+HTTP+AUTH+Issues" title="Possible Solution to HTTP AUTH Issues">Possible Solution to HTTP AUTH Issues</a></li><li><a href="http://wiki.javascud.org/display/SEL/Release+Process" title="Release Process">Release Process</a></li><li><a href="http://wiki.javascud.org/display/SEL/Selenium-RC+and+Continuous+Integration" title="Selenium-RC and Continuous Integration">Selenium-RC and Continuous Integration</a></li><li><a href="http://wiki.javascud.org/display/SEL/Specifications+for+Selenium+Remote+Control+Client+Driver+Protocol" title="Specifications for Selenium Remote Control Client Driver Protocol">Specifications for Selenium Remote Control Client Driver Protocol</a></li><li><a href="http://wiki.javascud.org/display/SEL/TODO+for+first+Selenium+Release" title="TODO for first Selenium Release">TODO for first Selenium Release</a></li><li><a href="http://wiki.javascud.org/display/SEL/Windows+Registry+Support" title="Windows Registry Support">Windows Registry Support</a></li></ul></li></ul></li>
				<li>Selenium On Rails </li>
		</ul>
		<h3>
				<a name="Home-4.%E7%9B%B8%E5%85%B3%E8%B5%84%E6%BA%90%EF%BC%88%E9%83%A8%E5%88%86%EF%BC%89">
				</a>4. 相关资源（部分）</h3>
		<p>
				<span class="nobr">
						<a href="/raimundox/archive/2006/08/04/61860.html" title="Visit page outside Confluence" rel="nofollow">徐昊(X)的：Selenium Better Pratice <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a>
				</span>
				<br />
				<span class="nobr">
						<a href="http://calvin.javaeye.com/blog/27298" title="Visit page outside Confluence" rel="nofollow">江南白衣的：Selenium--透明反复推介的集成测试工具(Pragmatic系列) <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a>
				</span>
				<br />
				<span class="nobr">
						<a href="http://wiki.javascud.org/display/springs/SeleniumRefrence" title="Visit page outside Confluence" rel="nofollow">cac翻译的：selenium参考手册 <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a>
				</span>
				<br />
				<span class="nobr">
						<a href="http://wiki.springside.org.cn/display/springside/Selenium" title="Visit page outside Confluence" rel="nofollow">SpringSideTeam的Selenium指南 <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a>
				</span>
				<br />
				<span class="nobr">
						<a href="http://www-128.ibm.com/developerworks/cn/java/wa-selenium-ajax/" title="Visit page outside Confluence" rel="nofollow">IBM的用 Selenium 自动化验收测试 <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a>
				</span>
				<br />
				<span class="nobr">
						<a href="http://www.testearly.com/2006/10/04/selenium-using-selenium-ide-selenium-remote-control-and-ant/" title="Visit page outside Confluence" rel="nofollow">Fit(table)+RC的方式使用Selenium <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a>
				</span>
				<br />
				<span class="nobr">
						<a href="http://wiki.openqa.org/display/SRC/Home" title="Visit page outside Confluence" rel="nofollow">OpenQA的Selenium Remote Control Wiki <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a>
				</span>
				<br />
				<span class="nobr">
						<a href="http://wiki.openqa.org/display/SEL/Home" title="Visit page outside Confluence" rel="nofollow">OpenQA的Selenium Core Wiki <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a>
				</span>
				<br />
				<span class="nobr">
						<a href="http://wiki.openqa.org/display/SIDE/Home" title="Visit page outside Confluence" rel="nofollow">OpenQA的Selenium IDE Wiki <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a>
				</span>
				<br />
				<span class="nobr">
						<a href="http://wiki.openqa.org/display/SOR/Home" title="Visit page outside Confluence" rel="nofollow">OpenQA的Selenium Selenium on Rails Wiki <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a>
				</span>
				<br />
				<span class="nobr">
						<a href="/Nicholas/archive/2006/11/02/78725.html" title="Visit page outside Confluence" rel="nofollow">Nicholas的用Selenium进行功能测试 <sup><img class="rendericon" src="http://wiki.javascud.org/images/icons/linkext7.gif" border="0" height="7" align="absMiddle" alt="" width="7" /></sup></a>
				</span>
				<br />
				<span class="nobr">
						<a href="/iamtin/archive/2006/10/30/78137.html" title="Visit page outside Confluence" rel="nofollow">Tin的Selenium做功能测试的一点讨论</a>
				</span>
		</p>

          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/35116#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></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 13 Nov 2006 15:31:00 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/35116</link>
        <guid>http://tin.javaeye.com/blog/35116</guid>
      </item>
      <item>
        <title>Selenium做功能测试的一点讨论(061102 update)</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/35117" style="color:red;">http://tin.javaeye.com/blog/35117</a>&nbsp;
          发表时间: 2006年10月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          先后和nemo、limo、raimudox、nicholas讨论：<br />selenium的Remote Control比较适合在需求阶段就撰写（当然Fit方式也可以先于实现写），作为验收的测试。好处是RC对重构支持相对好一些，而且你可以换Agent，也可以做浏览器兼容测试。（但是由于RC的限制，兼容侧试兼容性并不好：）<br />按照raimudox所说RC是更加Pragmatic的实践，更能体现敏捷软件开发的测试先行的特性。功能测试可以说是沟通用户与开发者的最佳契约。<br />Selenium IDE录制script适合作为基线保留（指先实现需求，后录制测试这样的顺序），作为某次重构之前的样本。或者说，如果觉得手写测试脚本太麻烦，而喜欢本末倒置（没有贬义，纯技术上）的人设计的。更现实的说，这很有用，比如一个项目从一半开始敏捷改造，引入功能测试、单元测试，对以后的迭代进行基线的衡量，给新引入的CI（持续集成）一个更有实际意义的测试保障，用Selenium IDE帮助生成一下Script，然后再使用RC或者直接用Core执行一下都是不错的实践。而Fit方式（这里指先于应用实现就写出来的基于html/table的Fit式测试），相对吸引力差一些，因为工作量与RC相仿，重构支持比较差，而且没有DSL风格的封装，读起来相对费解一些。<br />还有，据Nicholas同学实践，Selenium IDE所录制的script在IDE中执行比RC方式兼容性要好，尤其对于跨域的情况，RC很有可能是无法工作的。还有一个问题，就是Selenium实际上是ThoughtWorks和BEA牵头的项目，TW负责Core，目前Core的代码发展的必较快，而RC由BEA负责，发展比较缓慢，所以，有些时候选择也就成为无奈了。<br />061102补充：<br />1、Selenium目前有做不到的地方：例如&lt;input type="file"/&gt;的情况，由于安全问题，浏览器是不允许通过javascript置里面的value的，所以selenium在此时会处于无能为力的情况。比较郁闷。虽然强行修改如Mozzila的安全属性可以办，但那不是好办法。<br />2、对于拥有复杂的Ajax widget的应用测试可能会非常麻烦，因为需要写很多javascript api在测试里面，对重构支持差（如api发生变化修改unit test很麻烦，而且可能出现需要对你的测试进行测试的尴尬情况）。当然对于大部分的ajax应用Selenium都是很好的选择。<br />3、大家都很看好的Remote Control方式发展比较慢，API还不够友好（经常抛出奇怪的异常），Bug还是比较多。所以还需要耐心等待，要多些像我们这样的小白鼠:D<br /><font color="#0000ff">推荐大家看看我的同事nicholas的这篇：</font><a href="/Nicholas/archive/2006/11/02/78725.html" class="singleposttitle" id="viewpost1_TitleUrl"><font color="#0000ff">用 Selenium 进行功能测试</font></a><br /><font color="#a52a2a">浓缩一下：<br />1、何时、何目的来用Selenium选择不同。RC、Fit适合从需求阶段就开始写。而IDE录制则适合后补。<br />2、重构支持。RC重构友好一些。Fit重构不友好。<br />3、IDE目前限定于FF，做跨浏览器RC比较好。但是IDE录制后的代码很方便转为RC方式。<br />4、跨域兼容性问题，IDE解决的比较好。</font>
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/35117#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></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 30 Oct 2006 21:33:00 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/35117</link>
        <guid>http://tin.javaeye.com/blog/35117</guid>
      </item>
      <item>
        <title>java.lang.UnsatisfiedLinkError: no swt-win32-3232 in java.library.path的解决</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/35118" style="color:red;">http://tin.javaeye.com/blog/35118</a>&nbsp;
          发表时间: 2006年09月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          我用的是Eclipse 3.2，操作系统是WinXP SP2。<br />需要把eclipse\plugins\org.eclipse.swt.win32.win32.x86_3.2.0.v3232m.jar里面的swt-win32-3232.dll解压缩出来拷贝到WINDOWS\SYSTEM32里面。<br />然后就OK了。<br />是在Eclipse里面运行Springside的ANT Task的时候，如果需要通过console输入交互信息，则会报这个错。应该是因为Eclipse的console依赖于那个本地库，而平常我们安装Eclipse不会拷贝那个库。大概如此了。
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/35118#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></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 11 Sep 2006 15:10:00 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/35118</link>
        <guid>http://tin.javaeye.com/blog/35118</guid>
      </item>
      <item>
        <title>《OSGi实战》读后感</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/35119" style="color:red;">http://tin.javaeye.com/blog/35119</a>&nbsp;
          发表时间: 2006年09月06日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          
		<p>看了下BlueDavy的OSGi实战这篇OpenDoc，很感谢BlueDavy同学！<br />例子举的是一个User Login的Case，例子很简单，让我们从中领略了OSGi的风情。这个Doc中的例子都是围绕Equinox展开的，它是Eclipse 3.1以后的核心实现，也就是说现在的Eclipse是个OSGi架构。<br />从架构上来说OSGi和SOA如出一辙，都强调面向服务，而OSGi似乎对热切换和契约管理比较着重，也就是说OSGi更现实，它强调的是一种实际的合约标准。产生的结果是差不多的，就是系统模块之间的高度解藕。<br />可以看OSGi的Core Framework，最内层是L0：运行环境（就是语言平台或者解释平台一类的环境），然后是OSGI的L1：模块，L2：生命周期管理，L3：服务注册。<br />我认为这种架构也基本上是一个SOA需要关注的几个问题。<br />L1是实现OSGi的基础，在Java下提供了类加载机制，使系统能够模块化。个人感觉类似原来Eclipse中的微内核。<br />L2是解决模块之间依赖关系的最基本工作单位，负责初始化、停止、更新等操作，这样模块能够活起来，同时在这些过程中可以手动维护依赖关系，也是模块协作的基础。<br />L3则是协作的合同签署场所，应该是L2的扩展，使模块之间能够按照契约工作。我觉得更形象地说就是路由器，模块间的动态依赖可以很好地通过它来解决，让OSGi可以动起来。<br />拥有了这几层，我想我们完全可以理解为一个SOA的实现，当然更细化。应该是一种新的组合应用的方式。<br />白嘴说肯定没有BlueDavy的文章好，大家还是去看看那篇文档。</p>
		<p>说说遗憾：<br />1、OSGi在B/S架构中还不好应用。虽然例子是B/S的，可是居然是Servlet模型，里面解释了目前Equinox项目也在扩展应用服务器支持和JSP支持等，可是起码目前还不成熟。<br />2、模块的粒度很成问题。目前OSGi的契约机制与java interface机制对比一下。OSGi不可能完全取代本地的interface式的解藕，当然人家也没这么说。只使我担心过渡设计后，过细的Bundle肯定会得不偿失，所以需要有人设计/计划这个粒度。这个可能与基于Web services的SOA架构面临类似的问题，需要好的架构师。<br />3、文档不友好么？说实话，很感谢BlueDavy和OSGi观察者那些大牛的贡献。但是感觉production的样例工程还是很难搞到（其实Eclipse plugins的例子满多哈，可惜没啥文档，需要硬着头皮看），对应的指导文档还没出现。BlueDavy提供的servlet实现我们不可能跟上，毕竟简单也是一种需求。（那谁说过度设计比设计不足更可怕，那个我不是唱反调，我希望我们都能找到那个sweet point，有个好的参照那最好不过了）。<br />4、由于思想先进，在某些人看来是阳春白雪。估计不少人还是埋头下里巴人。观望态度。</p>
		<p>结束，又是流水账，大家拍砖。</p>

          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/35119#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></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 06 Sep 2006 11:27:00 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/35119</link>
        <guid>http://tin.javaeye.com/blog/35119</guid>
      </item>
      <item>
        <title>看看Springside的DAO和Manager</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/35120" style="color:red;">http://tin.javaeye.com/blog/35120</a>&nbsp;
          发表时间: 2006年09月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          
		<strong>内容太乱，如果懒得看请只看粗体部分就可以了</strong>
		<img src="/Emoticons/74_74.gif" border="0" height="19" width="19" />
		<br />springside的一个特点就是manager继承自DAO，其实这是个名字上的问题。<br />Java EE一直强调分层架构，在Web部分比较典型的就是前端MVC、中间Business、后面持久化。而Manager对应business，持久化由于实现替换的需求一般使用DAO模式。<br />先分析一下在轻量化的Java EE下面他们存在的意义：<br />1、有的人说过在Web项目中Manager和DAO是同意的，尤其是在透明ORM存在下，DAO由于往往是CRUD的实现场所，而Manager却往往是薄薄的一层门面，很多人就在质疑两者的合并问题。<strong>可是robbin曾经进行过一个精辟的分析，虽然两者做的看起来差不多，可是两者的事务属性却不一样，Manager应该有清晰的事务界限，而DAO不应关心于此。</strong>也就是说Manager可能会将几个DAO方法组合调用，然后封装在一个事物中。这样说明确了两者的一个重要区别，我们也能体会在使用声明事务的时候有一个分明的事务界限是很有意义的，否则就有可能把Manager中的一个事物拆分，这样实际上就错误了。<br />2、有些人质疑透明ORM存在的情况下DAO存在的意义，因为透明ORM基本已经隔离了不同数据库的方言区别。这个也很简单，Rod大叔分析过。<strong>透明ORM存在的情况下DAO起到了隔离透明ORM与EJB或者JDBC实现的作用</strong>，这几种实现实际上是应该考虑到的。<br />3、还有一个问题，就是DAO是否应该隐藏透明ORM的API。因为前面说到了DAO起到隔离实现的作用，似乎应该隐藏特定API。可是某大叔也说过，完全隔离不可能，修改底层实现而不修改上层API也不划算（应该说往往费力不讨好），其实就是Rod大叔的思想，我们宁可提供各种support或者template，但是不强求抽象出各种实现。所以<strong>DAO的实现即使依赖于部分Hibernate API也不是错</strong>。<br />上面纯属贫嘴，知道的朋友们不要嫌弃。<br />说说Springside的实现。<br />其实，<strong>实际上Springside使用的就是经典的GenericHibernateDAO+无Manager的实现</strong>。这么说在否定前面所说，不过其实这都是文字游戏：D<br />首先，对于Hibernate为主的实现下，DAO使用Generic是很方便的。Spring的hibernate template受累于向JDK1.4兼容，所以没有用generic，但是实际上DAO是generic的经典应用。在Hibernate网站上有过讨论。现在Springside使用的GenericHibernateDAO已经进化的非常先进了。<br /><strong>Springside实际上有Manager，我说没有其实是指它的Manager继承自DAO。</strong>看似乱伦，但实际上非常合理。<strong>前面说了Manager与DAO的很大区别在于事物范畴，使用继承后，两者之间就可以分离，可以通过Spring的AOP将事物属性配置在Manager上，也就解决了问题。这应该说也是template模式的标准应用。本来Manager就有很多方法可以通过模板实现，而DAO和Generic就很好地解决了他们之间的模板关系。<br /></strong>我们先看看具体的设计：<br />以CustomerManager为例，我们看看类签名：<br /><font color="#ff0000">CustomerManager extends BaseHibernateDao&lt;Customer&gt;<br />BaseHibernateDao&lt;T&gt; extends AbstractHibernateDao&lt;T&gt;<br />AbstractHibernateDao&lt;T&gt; extends HibernateDaoSupport</font><br /><strong>三层结构：<br /><font color="#0000ff">第一层</font>AbstractHibernateDao继承自大家都熟悉的Spring的HibernateDaoSupport，这一层的主要作用是扩展Generic，这样一方面减少了强行类型转换的啰嗦，一方面使DoaminClass的信息通过Generic继承透明声明。这一层还有一个作用，就是说不管你是否使用Generic，你的应用程序最好也在这里增加一层继承，作用是在需要的时候你可以在这里扩展DAO模版：D<br /><font color="#0000ff">第二层</font>是BaseHibernateDao。这个其实是springside很自豪的一个地方，可以理解，因为这里真的花了很多的代码，而且相当精妙。</strong>不卖官子，其实<strong>这里主要给DAO扩展了分页支持</strong>，这显然是异常重要的，大家google一下就知道分页在Web应用中的重要性了，<strong>基本上你看一个Java Web Framework都会看到它对自己分页特性的支持方式介绍，或者说分页的实现风格已经是Java Web Framework实现优劣的一个标准了。</strong>应该说，<strong>springsdie的分页实现来自javaeye上的一个经典的讨论</strong>，robbin等牛在里面仔细讨论了用DetachedCriteria实现分页查询，后面引出bug，然后解决，希望没看过的朋友都看看。（应用Hibernate3的DetachedCriteria实现分页查询|http://forum.javaeye.com/viewtopic.php?t=14657&amp;postdays=0&amp;postorder=asc&amp;start=0）<br /><strong>springside的实现是集大成者，考虑到了Criteria（不仅是DetachedCriteria）、HQL、collection.size()几种不同实现，也考虑到了使用Criteria的时候Order造成的问题</strong>（这个在我的Blog中给DetachedCriteria擦屁股那个里面讨论过）。<br /><strong><font color="#0000ff">第三层</font>就是具体的Manager，它是用Generic继承传递了DoaminModel的类信息。由于大部分CRUD已经在DAO里面实现了，所以Manager只需要实现一些需要特殊实现的method的就可以了，简单的应用中Manager里面经常是空空如也。<br /></strong><br /><strong>其中，BaseHibernateDao的实现相对复杂，主要因为里面包装了分页实现。大家可以具体看看CriteriaPage和HqlPage两个类，这是精妙所在。</strong>但是在这一层还有个<strong>CriteriaSetup是个比较奇怪的东西</strong>，一开始没太看明白是做什么用的。后来问了百衣，他说这是为了解决search问题提供的，从jsp传递过来的东西在Controller层可以通过util（<strong>这里springside很CoC，它把search_开头的东西自动作为“=”的查询条件传给Manager，但目前的缺陷在于只能默认处理“=”，其它方式需要扩展</strong>）处理查询条件传给Manager，但是这个东西设计得有点怪，白衣也对它不满意，说在Springside 2.0里面要重构掉，但还没有什么好的想法，大家又建议可以提给他。<br />我认为传递查询信息大概需要这样的设计：<br />1、在Controller里面不暴露hibernate API，但是能够顺利将参数传递到Manager。<br />2、Manager直接传递，或者将这些参数转换为Hibernate API后再传递，因为这正是manager需要负责的商业逻辑之一。<br />3、DAO里面最好不要做那么多转换，毕竟它是持久层。 <br />当然，目前符合这个要求，只是限定于Map是二元容器，不能存3元的查询条件（key、value、type）。所以可能下一步会补足。<br />分析到这里，springside的DAO的结构大概就是这样的。<br />感觉这和我们Team上个项目的结构很象（不好意思，没说有springside那么好），有一点区别。<br />由于Manager继承自DAO，我们让DAO多了很多方法，主要就是想提供方便。尤其是<br /><strong><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="/Images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"> T loadById(Class</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"> persistenceClass, Serializable id);<br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"> T loadByProperty(Class</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"> persistenceClass, String propertyName, Object value);<br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"> List</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">listAll(Class</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"> persistenceClass);<br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"> List</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">listByProperty(Class</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"> persistenceClass, String propertyName, Object value);<br /><img src="/Images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"> List</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">listAllOrderBy(Class</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"> persistenceClas, </span><span style="COLOR: #0000ff">boolean</span><span style="COLOR: #000000"> isAsc, String orderByPropertyName);</span></div>可是springside并没有提供这样的东西，有点遗憾。<br />这些东西经常用到，如果使用了会减少了很多时候遇到的Manager里面注入其他Domain的DAO，却只是为了load或者listAll的问题（同时用generic）：D</strong><br />今天有点别的事，先说到这里，好好想想，看有什么问题过两天在说说。<strong>这Blog太流水账，太空洞，大家见谅。</strong>
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/35120#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/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 05 Sep 2006 21:52:00 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/35120</link>
        <guid>http://tin.javaeye.com/blog/35120</guid>
      </item>
      <item>
        <title>Spring与EJB 3对比读后感</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/35121" style="color:red;">http://tin.javaeye.com/blog/35121</a>&nbsp;
          发表时间: 2006年09月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          今天TSS和InfoQ都转了一篇Spring与EJB3的读后感，我就看了下，标题和介绍满吸引人的。内容嘛其实有点不过瘾，但是先记录下来吧。<br /><a href="http://www.devx.com/Java/Article/32314/0/page/1">http://www.devx.com/Java/Article/32314/0/page/1</a><br /><br />总的来看Spring+Hibernate与JPA很相似，它们都是基于pojo的持久化。<br />Hibernate Session和JPA Entity Manager基本上等价，但是要记住他们的两个重要区别。Hibernate session是一个实体缓存也是一个ORM引擎的接口。而JPA中这两个概念是分开的。Persistence context作为缓存而entity manager则作为ORM引擎的接口。<br />当然还有显而易见的区别，Spring+Hibernate偏向使用XML配置映射，而JPA偏向使用Annotation，虽然两者都有XML和注释两种实现。<br />还有，JPA是一个标准，而Spring是对不同实现的抽象，两者的方向是不同的。JPA的方式更彻底，Java传统中都是这样的。<br />JPA的主要商业实现有Hibernate、Kodo、Toplink，被巨鳄们看好。<br />后面，说到了关于Cache和Transaction管理的问题，由于Spring的草根特性，为了兼容实现，它使用Tread local这种编程式的方式。而EJB 3.0则由容器自动完成这些过程。而且EJB 3.0提供了不同的persistence context范畴，可以比较方便的管理持久数据的生命周期。不过，这个观点很难说，因为如果你把Spring也看成一种容器，那么这Thread local对于你来说也是透明的，可以认为差不多。<br />关于EJB 3.0对生命周期的规定，让持久化的概念更清楚了，如果这些东西能够通过声明而不是编码来实现应该是惬意的，可是，问题就是很多程序员一般就喜欢自己控制，不喜欢那么透明，所以EJB一直以来兴建的这些漂亮模型总是只有少数人使用，不是么？<br />在事务方面，由于两者都支持生命性事务，所以程序本身看起来基本一样。<br />区别在于配置。Spring还是偏向XML，并且事务作为Spring对AOP应用的经典样例，transaction完全作为附加语义，可以通过配置替换各种事务实现，从JDBC、Hibernate到JTA，显然这是从编程者角度考虑的，门槛很低。<br />而EJB 3.0则只支持JTA，这就需要容器的支持，当然跨多资源的事务往往是企业级项目的特性，所以这种思路可以理解。而且现在也有很多开源的JTA实现了，它们完全可以让你的应用在商业EJB容器外运行。还要提一点，EJB3默认是配置上事务的，需要声明才可覆盖，这说明了EJB3对于企业应用的态度。<br />在JTA事务可以通过声明就以横切关注点的形势注入的时候，JTA的成本已经下降了，所以一开始就用这种API完全可行。<br />这篇文章中关于状态的地方我有点不太理解，里面说Spring的prototype等价于EJB的SFSBs，实现stateful。<br />EJB 3.0在这方面无疑是强大的，因为本身在这方面它就是个容器规范，Java EE容器都会提供这种高级的生命周期管理，并且把生命周期作为变成模型中非常重要的一部分。所以结果就是EJB 3.0在这方面领先于Spring，声名简单，并且从实现的方式上来说，EJB 3.0在性能上和可伸缩性上有明显的优势。Spring在性能伸缩或者说分布部署的时候应该说是捉襟见肘的，Terracotta也许可以解决些，但还……<br />应该说，实际上Spring提供的prototype就是new的另外一种实现，只不过它会经过Spring装配，所以它叫做prototype，也就是“原型”，Spring每次回new出一个新的，按你的要求。当然，由于是类似new，所以Spring通过代理的方式管理起生命周期，也就能模拟出session、request、global session的statefull，不过这些功能显然不算强项，在Spring 2.0中加强了（以前只有singleton和prototype），但依然不支持事务范畴，这就明显不如EJB 3.0了。但是，回想Spring的编程哲学，它不要解决这种问题，这种问题留给容器解决:D<br />文章最后的总结比较官腔，基本上就是在说Spring灵活，EJB 3.0强大简单，它们各有优缺点，所以应该结合起来使用，具体大家可以看看原文。
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/35121#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></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 04 Sep 2006 20:45:00 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/35121</link>
        <guid>http://tin.javaeye.com/blog/35121</guid>
      </item>
      <item>
        <title>在Eclipse里面运行Springside的Test</title>
        <author>Tin</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://tin.javaeye.com">Tin</a>&nbsp;
          链接：<a href="http://tin.javaeye.com/blog/35122" style="color:red;">http://tin.javaeye.com/blog/35122</a>&nbsp;
          发表时间: 2006年08月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Springside里面的Test现在已经比较丰富，而且徐x贡献了基于selenium rc的functional test，这些东西是很好的学习资料。尤其是functional test，让QA的任务可以在Java里面实现了，而且是基于浏览器的测试。<br />推荐看看rainmundox的<a href="/raimundox/archive/2006/08/04/61860.html" id="viewpost1_TitleUrl"><font color="#808080">Selenium Better Pratice</font></a><br /><br />但是首先却遇到了问题：<br />在Eclipse里面运行functional-test这个target的时候发现报错（test target叶出错的）：<br />Could not create task or type of type: junit.<br />我就想当然的把junit-3.8.1.jar拷贝到我的%ANT_HOME%/lib下面了，结果问题依旧。<br />然后google了下，发现是这样的：<br />“该信息表明 Ant 没有找到任务或任务所依靠的类。 Ant 试图加载 Junit JAR 文件。当在 IDE 外部使用 Ant 时，应该把 junit.jar 放在 %ANT_HOME%/lib 文件夹。使用 Eclipse 时该任务无法工作，因为它使用自身版本的 Ant 。所以 junit.jar 必须放在 Eclipse 的 Ant 文件夹中，即 %ECLIPSE_HOME%/plugins/org.apache.ant_1.6.2/lib （很显然，不同版本的 Eclipse 会有不同版本的 Ant ）。”<br />在Eclipse 3.2里面已经是ant_1.6.5了，拷贝过去问题还是没有解决。<br />然后走了不少弯路。<br />最后，发现其实是这样的。上面提示了Eclipse用的自己的ant，这个时候你拷贝junit-3.8.1.jar过去并不起作用，因为ant设置不会自动扫描那个目录的/lib。<br />解决方法目前看最好的就是在Eclipse的Window-&gt;Preferences-&gt;Ant-&gt;Runtime-&gt;Classpath里面Ant_Home你指定一下就可以了，可以是你自己的Ant或者Eclipse/plugins里面的ant，这没关系，然后它就会自动扫描相应/lib目录了（之前你应该已经把junit-3.8.1.jar拷贝过去了）。<br />然后就搞定了。注意，只有在Eclipse运行Ant才有可能遇上这个问题:D<br /><br />呵呵，问题搞定了，先review一下Test，看看有什么能解决的。然后我想先分析一下SpringSide的DAO、Manager这种结构，明后天。
          <br/>
          <span style="color:red;">
            <a href="http://tin.javaeye.com/blog/35122#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></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 31 Aug 2006 13:02:00 +0800</pubDate>
        <link>http://tin.javaeye.com/blog/35122</link>
        <guid>http://tin.javaeye.com/blog/35122</guid>
      </item>
      <item>
        <title>关于RoR学习一点胡言乱语</tit