提升代码质量工具学习总结

一、 Ant-构建工具
a) 工具简介
Ant是一个用于简单或复杂Java工程的自动化 构建、部署工具,它对于那些具有分布式开发团队或者相信通过频繁的构建来进行不间断集成的公司尤其有用。对于那些建立传统全Java应用程序以及那些使用 HTML、JSP和Java servlets创建Web应用程序的公司来说,Ant极具价值。无论你的Java开发者使用什么操作系统、集成开发环境或者构建环境,Ant都可以将你 的工程集合在一起,用于那些重要的构建。Ant也能够自动化并且同步文档部署,这通常发生在软件开发过程中的没有正式文档和文档比较混乱的部分。
在构建和部署Java应用程序的时候,Ant处理着大量有用的任务。最基本的任务包括添加和移除目 录、使用FTP拷贝和下载文件、创建JAR和ZIP文件以及创建文档。更高级的特性包括用源代码控制系统诸如CVS或者SourceSafe来检查源代 码、执行SQL查询或脚本、将XML文件转换为人能识别的HTML,以及为远程方法调用生成stub(存根)文件。
Ant和Make(非常著名的构建工具,很多C语言开发人员都使用它)之间有什么不同?Ant是为Java而创建,带有属于其自身的、独特的范例, 具有可移植性。而Make依赖于固定的操作系统命令(因此一个运行在微软Windows下的Make文件对于使用UNIX的开发者来说毫无用处),利用 Ant构建的纯Java工程是可移植的,因为Ant本身就是用Java编写的,并且Ant bulidfiles使用XML语法。
b) 环境配置
window中设置ant环境变量:
ANT_HOME    D:/ant 1.6
path    D:/ant 1.6/bin
classpath    D:/ant 1.6/lib
linux中设置ant环境变量:
root用户登陆,用vi编辑器修改root目录下的".bash_profile"属性文件,假设Ant安装在/user/local/ant目录下,要在文件的最后加上"exportANT_HOME=/user/local/ant"和"export PATH=$PATH:$ANT_HOME/bin"

c) 使用案例(获取SVN服务器上源代码,编译下载的JAVA源代码)
第一步、从Ant官方网下载apache-ant-1.8.2包,并解压到指定目录,配置环境变量:ANT_HOME,PATH。配置完成之后通过输入命令ant –version查看ant版本信息,可以显示证明ant配置成功。注:ANT配置之前确保JAVA环境配置成功。
第二步、从SVN官方网下载svnant包,并解压到指定目录,配置环境变量:SVN_HOME,PATH或将SVN解压文件夹lib下的所有jar包拷贝到apache-ant/lib目录下。注:i)使用ant编译web项目源代码时,请将tomcat/lib目录下catalina-ant.jar, jsp-api.jar拷贝到apache-ant/lib目录下。ii)ant配置的计算机系统时间必须大于或等于SVN服务器上时间,否则会报:Warning: SomeClassName.java modified in the future异常
第三步、编写下述ant脚本:
<?xml version="1.0" encoding="UTF-8"?>
<project name="oa" default="checkout" basedir=".">
<!--===================== svn版本控制器类 ====================-->
<taskdef name="svn"
classname="org.tigris.subversion.svnant.SvnTask" />
<!--=============== 从SVN服务器下载源代码 ==================-->
<target name="checkout" >
<svn javahl="true" username="xxx" password="xxxx">
<checkout url="${remote.url}" destPath="${local.dir}" />
</svn>
</target>
<!--====================== 编译 ===========================-->
<target name="compile"
description="Compile main source tree java files into class files">
<javac srcdir="${src.dir}:${src.test.dir}" encoding="utf-8"
destdir="${class.dir}/classes" source="1.6" target="1.6" debug="true"
deprecation="false" optimize="false" failonerror="true"
includeantruntime="on">
<classpath refid="system-classpath" />
</javac>
</target>
</project>
第四步、执行ant脚本 ant –buildfile ant脚本文件 targetName
二、 Checkstyle-代码审查工具
a) 工具简介
CheckStyle是SourceForge下的一个项目,提供了一个帮助JAVA开发人员遵守某些编码规范的工具。它能够自动化代码规范检查过程,从而使得开发人员从这项重要,但是枯燥的任务中解脱出来;CheckStyle工具还可以对审查的JAVA内容生成报告。

b) 环境配置
从checkstyle官方网下载checkstyle-5.4-bin.zip包,并解压到指定目录。Ant脚本文件中指定该目录即可。同时也可以将checkstyle集成到elicpse代码开发工具中,更加方便开发人员进行代码编写规范检查。
c) 使用案例
<taskdef name="checkstyle"
classname="com.puppycrawl.tools.checkstyle.CheckStyleTask"
classpathref="checkstyle-classpath" />

<target name="checkstyle" description="检查代码编写规范,并生成编码检查报告">
<mkdir dir="${report}/checkstyle" />
<checkstyle config="${checkstyle.dir}/sun_checks.xml"
failureProperty="checkstyle.failure" failOnViolation="false">
<formatter type="xml"
tofile="${report}/checkstyle/checkstyle_report.xml" />
<fileset dir="${local.dir}/src" includes="**/*.java" />
</checkstyle>
<style in="${report}/checkstyle/checkstyle_report.xml"
out="${report}/checkstyle/checkstyle_report.html"
style="${checkstyle.dir}/contrib/checkstyle-frames.xsl" />
</target>
build完成之后在checkstyle目录会生成一份检查报告。

三、 Findbug-静态代码分析工具
a) 工具简介
Findbugs是一个静态分析工具,它检查类或者JAR 文件,将字节码与一组缺陷模式进行对比以发现可能的问题。Findbugs自带检测器,其中有60余种Bad practice,80余种Correctness,1种 Internationalization,12种Malicious code vulnerability,27种Multithreaded correctness,23种Performance,43种Dodgy。
Findbugs是一个在java程序中查找bug的程序,它查找bug模式的实例,也就是可能出错的代码实例,注意Findbugs是检查java字节码,也就是*.class文件。其实准确的说,它是寻找代码缺陷的,很多我们写的不好的地方,可以优化的地方,它都能检查出来。例如:未关闭的数据库连接,缺少必要的null check,多余的 null check,多余的if后置条件,相同的条件分支,重复的代码块,错误的使用了"==",建议使用StringBuffer代替字符串连加等等。而且我们还可以自己配置检查规则(做哪些检查,不做哪些检查),也可以自己来实现独有的校验规则(用户自定义特定的bug模式需要继承它的接口,编写自己的校验类,属于高级技巧)。
Bad practice 坏的实践
一些不好的实践,下面列举几个:
HE: 类定义了equals(),却没有hashCode();或类定义了equals(),却使用
Object.hashCode();或类定义了hashCode(),却没有equals();或类定义了hashCode(),却使用Object.equals();类继承了equals(),却使用Object.hashCode()。
SQL:Statement 的execute方法调用了非常量的字符串;或Prepared Statement是由一个非常量的字符串产生。
DE: 方法终止或不处理异常,一般情况下,异常应该被处理或报告,或被方法抛出。
Correctness 一般的正确性问题
可能导致错误的代码,下面列举几个:
NP: 空指针被引用;在方法的异常路径里,空指针被引用;方法没有检查参数是否null;null值产生并被引用;null值产生并在方法的异常路径被引用;传给方法一个声明为@NonNull的null参数;方法的返回值声明为@NonNull实际是null。
Nm: 类定义了hashcode()方法,但实际上并未覆盖父类Object的hashCode();类定义了tostring()方法,但实际上并未覆盖父类Object的toString();很明显的方法和构造器混淆;方法名容易混淆。
SQL:方法尝试访问一个Prepared Statement的0索引;方法尝试访问一个ResultSet的0索引。
UwF:所有的write都把属性置成null,这样所有的读取都是null,这样这个属性是否有必要存在;或属性从没有被write。 Internationalization 国际化 当对字符串使用upper或lowercase方法,如果是国际的字符串,可能会不恰当的转换。
Malicious code vulnerability 可能受到的恶意攻击
如果代码公开,可能受到恶意攻击的代码,下面列举几个:
FI: 一个类的finalize()应该是protected,而不是public的。
MS:属性是可变的数组;属性是可变的Hashtable;属性应该是package protected的。
Multithreaded correctness 多线程的正确性
多线程编程时,可能导致错误的代码,下面列举几个:
ESync:空的同步块,很难被正确使用。
MWN:错误使用notify(),可能导致IllegalMonitorStateException异常;或错误的
使用wait()。
No:  使用notify()而不是notifyAll(),只是唤醒一个线程而不是所有等待的线程。
SC:  构造器调用了Thread.start(),当该类被继承可能会导致错误。 Performance 性能问题
可能导致性能不佳的代码,下面列举几个:
DM:方法调用了低效的Boolean的构造器,而应该用Boolean.valueOf(…);用类似
Integer.toString(1) 代替new Integer(1).toString();方法调用了低效的float的构造器,应该用静态的valueOf方法。
SIC:如果一个内部类想在更广泛的地方被引用,它应该声明为static。
SS: 如果一个实例属性不被读取,考虑声明为static。
UrF:如果一个属性从没有被read,考虑从类中去掉。
UuF:如果一个属性从没有被使用,考虑从类中去掉。
Dodgy 危险的
具有潜在危险的代码,可能运行期产生错误,下面列举几个:
CI: 类声明为final但声明了protected的属性。
DLS:对一个本地变量赋值,但却没有读取该本地变量;本地变量赋值成null,却没有读取该本地变量。
ICAST: 整型数字相乘结果转化为长整型数字,应该将整型先转化为长整型数字再相乘。
INT:没必要的整型数字比较,如X <= Integer.MAX_VALUE。
NP: 对readline()的直接引用,而没有判断是否null;对方法调用的直接引用,而方法可能返回null。
REC:直接捕获Exception,而实际上可能是RuntimeException。
ST: 从实例方法里直接修改类变量,即static属性。
b) 环境配置
从官方网下载findbugs压缩包,解压到指定目录,并将路径配置到ant脚本中。

c) 使用案例
<!--================== 运用findbug工具检查代码bug ================-->
<target name="findbug" depends="compile"
description="findbug工具检查代码bug,并生成bug报告">
<mkdir dir="${report}/findbug" />
<findbugs home="${findbugs.dir}"
excludefilter="${findbugs.dir}/findbug/findBugsexcludeFilter.xml"
output="html" outputFile="${report}/findbug/findbugs-report.html"
jvmargs="-Xmx512m" failonerror="true" reportlevel="low"
warningsproperty="bugs.found">
<!-- classes.dir是需要用findbugs检查的代码的class文件目录 -->
<class location="${class.dir}/classes" />
<!-- classpath refid="system-classpath" /> -->
<!--  以上定义上述类所依赖的类路径  -->
<!--  auxclasspath path="${findbugs.dir}/lib/findbugs-ant.jar" />-->
<!--  以上定义源代码的路径  -->
<sourcePath path="${local.dir}/src/main/java" />
</findbugs>
</target>

四、 Junit-单元测试框架
a) 工具简介
JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework)。Junit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。Junit是一套框架,继承TestCase类,就可以用Junit进行自动测试了。
JUnit是一个开放源代码的Java测试框架,用于编写和运行可重复的测试。他是用于单元测试框架体系xUnit的一个实例(用于java语言)。它包括以下特性:
1、用于测试期望结果的断言(Assertion)
2、用于共享共同测试数据的测试工具
3、用于方便的组织和运行测试的测试套件
4、图形和文本的测试运行器
另外junit是在极限编程和重构(refactor)中被极力推荐使用的工具,因为在实现自动单元测试的情况下可以大大的提高开发的效率,但是实际上编写测试代码也是需要耗费很多的时间和精力的,那么使用这个东东好处到底在哪里呢?笔者认为是这样的:
1、对于极限编程而言
  要求在编写代码之前先写测试,这样可以强制你在写代码之前好好的思考代码(方法)的功能和逻辑,否则编写的代码很不稳定,那么你需要同时维护测试代码和实际代码,这个工作量就会大大增加。因此在极限编程中,基本过程是这样的:构思-> 编写测试代码-> 编写代码-> 测试,而且编写测试和编写代码都是增量式的,写一点测一点,在编写以后的代码中如果发现问题可以较快的追踪到问题的原因,减小回归错误的纠错难度。
2、对于重构而言
  其好处和极限编程中是类似的,因为重构也是要求改一点测一点,减少回归错误造成的时间消耗。
3、对于非以上两种情况
我们在开发的时候使用junit写一些适当的测试也是有必要的,因为一般我们也是需要编写测试的代码的,可能原来不是使用的junit,如果使用 junit,而且针对接口(方法)编写测试代码会减少以后的维护工作,例如以后对方法内部的修改(这个就是相当于重构的工作了)。另外就是因为junit 有断言功能,如果测试结果不通过会告诉我们那个测试不通过,为什么,而如果是想以前的一般做法是写一些测试代码看其输出结果,然后再由自己来判断结果使用 正确,使用junit的好处就是这个结果是否正确的判断是它来完成的,我们只需要看看它告诉我们结果是否正确就可以了,在一般情况下会大大提高效率。
安装JUnit
安装很简单,先到以下地址下载一个最新的zip包:
下载完以后解压缩到你喜欢的目录下,假设是JUNIT_HOME,然后将JUNIT_HOME下的junit.jar包加到你的系统的CLASSPATH环境变量中,对于IDE环境,对于需要用到的junit的项目增加到lib中,其设置不同的IDE有不同的设置,这里不多讲。
如何使用JUnit写测试
最简单的范例如下:
1、创建一个TestCase的子类
  package junitfaq;
  import java.util.*;
  import junit.framework.*;
  public class SimpleTest extends TestCase {
  public SimpleTest(String name) {
  super(name);
  }
  2、写一个测试方法断言期望的结果
  public void testEmptyCollection() {
  Collection collection = new ArrayList();
  assertTrue(collection.isEmpty());
  }
注意:JUnit推荐的做法是以test作为待测试的方法的开头,这样这些方法可以被自动找到并被测试。
3、写一个suite()方法,它会使用反射动态的创建一个包含所有的testXxxx方法的测试套件
  public static Test suite() {
  return new TestSuite(SimpleTest.class);
  }
  4、写一个main()方法以文本运行器的方式方便的运行测试
  public static void main(String args[]) {
  junit.textui.TestRunner.run(suite());
  }
  }
  5、运行测试
  以文本方式运行:
  java junitfaq.SimpleTest
  通过的测试结果是:
  .
  Time: 0
  OK (1 tests)
Time上的小点表示测试个数,如果测试通过则显示OK。否则在小点的后边标上F,表示该测试失败。
每次的测试结果都应该是OK的,这样才能说明测试是成功的,如果不成功就要马上根据提示信息进行修正了。
如果JUnit报告了测试没有成功,它会区分失败(failures)和错误(errors)。失败是你的代码中的assert方法失败引起的;而错误则是代码异常引起的,例如ArrayIndexOutOfBoundsException。
以图形方式运行:
java junit.swingui.TestRunner junitfaq.SimpleTest
通过的测试结果在图形界面的绿色条部分。
以上是最简单的测试样例,在实际的测试中我们测试某个类的功能是常常需要执行一些共同的操作,完成以后需要销毁所占用的资源(例如网络连接、数据库连 接,关闭打开的文件等),TestCase类给我们提供了setUp方法和tearDown方法,setUp方法的内容在测试你编写的TestCase子 类的每个testXxxx方法之前都会运行,而tearDown方法的内容在每个testXxxx方法结束以后都会执行。这个既共享了初始化代码,又消除 了各个测试代码之间可能产生的相互影响。
JUnit最佳实践
Martin Fowler说过:“当你试图打印输出一些信息或调试一个表达式时,写一些测试代码来替代那些传统方法。”一开始,你会发现你总是要创建一些新的 Fixture,而且测试似乎使你的编程速度慢了下来。然而不久之后,你会发现你重复使用相同的Fixture,而且新的测试通常只涉及添加一个新的测试 方法。
你可能会写许多测试代码,但你很快就会发现你设想出的测试只有一小部分是真正有用的。你所需要的测试是那些会失败的测试,即那些你认为不会失败的测试,或你认为应该失败却成功的测试。
我们前面提到过测试是一个不会中断的过程。一旦你有了一个测试,你就要一直确保其正常工作,以检验你所加入的新的工作代码。不要每隔几天或最后才运行 测试,每天你都应该运行一下测试代码。这种投资很小,但可以确保你得到可以信赖的工作代码。你的返工率降低了,你会有更多的时间编写工作代码。
不要认为压力大,就不写测试代码。相反编写测试代码会使你的压力逐渐减轻,因为通过编写测试代码,你对类的行为有了确切的认识。你会更快地编写出有效率地工作代码。
下面是一些具体的编写测试代码的技巧或较好的实践方法:
1. 不要用TestCase的构造函数初始化Fixture,而要用setUp()和tearDown()方法。
2. 不要依赖或假定测试运行的顺序,因为JUnit利用Vector保存测试方法。所以不同的平台会按不同的顺序从Vector中取出测试方法。
3. 避免编写有副作用的TestCase。例如:如果随后的测试依赖于某些特定的交易数据,就不要提交交易数据。简单的回滚就可以了。
4. 当继承一个测试类时,记得调用父类的setUp()和tearDown()方法。
5. 将测试代码和工作代码放在一起,一边同步编译和更新。(使用Ant中有支持junit的task.)
6. 测试类和测试方法应该有一致的命名方案。如在工作类名前加上test从而形成测试类名。
7. 确保测试与时间无关,不要依赖使用过期的数据进行测试。导致在随后的维护过程中很难重现测试。
8. 如果你编写的软件面向国际市场,编写测试时要考虑国际化的因素。不要仅用母语的Locale进行测试。
9. 尽可能地利用JUnit提供地assert/fail方法以及异常处理的方法,可以使代码更为简洁。
10.测试要尽可能地小,执行速度快。
11.不要硬性规定数据文件的路径。
12.利用Junit 的自动异常处理书写简洁的测试代码,事实上在Junit 中使用try-catch 来捕获异常是没有必要的,Junit 会自动捕获异常。那些没有被捕获的异常就被当成错误处理。
13. 充分利用Junit 的assert/fail 方法
  assertSame()用来测试两个引用是否指向同一个对象
  assertEquals()用来测试两个对象是否相等
14. 确保测试代码与时间无关
15. 使用文档生成器做测试文档。
JUnit和ant结合
  ant 提供了两个 target : junit 和 junitreport 运行所有 测试用例 ,并生成 html 格式的报表
具体操作如下:
1.将 junit.jar 放在 ANT_HOMElib 目录下
2.修改 build.xml ,加入如下 内容: 
运行 这个 target ,ant 会运行每个 TestCase,在 report 目录下就有了很多 TEST*.xml 和 一些网页打开 report 目录下的 index.html 就可以看到很直观的测试运行报告,一目了然。
在Eclipse中开发、运行JUnit测试相当简单。因为Eclipse本身集成了JUnit相关组件,并对JUnit的运行提供了无缝的支持。
b) 环境配置
从官方网下载junit包,在ant脚本文件中引用相应路径。
c) 使用案例(运行单元测试用例并生成单元测试用例报告)
<target name="test" depends="compile"
description="run junit test">
        <!—测试报告目录-->
<mkdir dir="${report}/junit/xml" />
<mkdir dir="${report}/junit/html" />
<junit dir="." fork="true" printsummary="on"
haltonfailure="false" failureproperty="tests.failed"
showoutput="true">
<classpath refid="junit-classpath" />
<!—-指定编写后的class文件目录,包括单元测试类-->
<classpath location="${class.dir}/classes" />
<!-- 生成报告数据的格式,支持xml/brief/plain -->
<formatter type="xml" />
<batchtest todir="${report}/junit/xml">
<!—指定单元测试类目录-->
<fileset dir="${class.dir}/classes">
<include name="**/*Test.*" />
<include name="**/Test*.*" />
<exclude name="**/selenium/*.*" />
</fileset>
</batchtest>
</junit>
         <!—生成测试报告-->
<junitreport todir="${report}/junit/html">
<fileset dir="${report}/junit/xml">
<include name="*.xml" />
</fileset>
<report format="frames" todir="${report}/junit/html" />
</junitreport>
</target>

五、 Selenium-自动化测试框架
a) 工具简介
selenium 是openQA的一个用于Web应用程序测试的不错的开源工具。它是通 过javascrīpt来驱动测试的,真实在浏览器中运行测试,更接近用户的实际操作。它定位方式很灵活,可以直接用标签 ID,link等,当然最为灵 活并可靠的就是通过xpath 来定位,能过xpath几乎没有不可定位的元素。此外它支持各类主流浏览器IE,firefox,safari,而且是同 一个脚本就可搞定,这是它较为厉害的一点。当然它也有自己的缺点:不能处理非javascrīpt 类弹出窗口,跨网站处理不容易等。总的来说 selenium 是一个非常不错的web应该程序测试工具。
selenium本身亦有几个测试框 架,selenium IDE, selenium RC, selenium Grid,
selenium Core. 但总的来说脚本只有两种即是 通过程式语言编写的脚本及HTML脚本。其中较为常用的是selenium RC 及selenium IDE.  HTML 脚本编写较为简单易懂,但 不够灵活,维护亦不那么容易,局限性较大,适合于非技术人员或是于编程不太熟悉者。而程式语言编写的脚本刚比较灵活强大,而且可以提供较多种类语言编写如 java,c#,ruby等。所以建议有编程基础的同仁用selenium IDE 录制后保存为 程式语言脚本进行编辑维护。
具体如何使用期selenium进行测试呢?下面将简单介绍selenium IDE 的使用并较为详细地与同仁们探讨selenium RC的使用。
一、简介
1、Selenium是 ThoughtWorks 专门为 Web 应用而开发的自动化测试工具,适合进行功能测试、验收测试。
2、最大的优势有以下几点:
●可直接运行在浏览器之上,就像真实用户所做的一样。
●Selenium测试可以在 Windows、Linux 和 MacintoshAnd 等系统下的 Internet Explorer、Mozilla 和 Firefox 中运行。
●测试脚本的运行不需要依赖其它任何的工具。
3、可测试的内容
   ◆Browser兼容性测试:
     验证被测试Web引用是否可以在不同的系统和不同的浏览器下都可以正常的执行。
     同一个脚本不用修改即可在不同的环境下进行部署和执行测试。
   ◆系统功能测试:
     创建可复用的测试脚本,以便在性功能和模块追加后对原有内容进行应用功能回归测试。
4、Selenium的核心(browser bot)
是用 Javascrīpt 编写的。这使得测试脚本可以在受支持的浏览器中运行。browser bot 负责执行从测试脚本接收到的命令,测试脚本要么是用 HTML 的表布局编写的,要么是使用一种受支持的编程语言编写的。
5、Selenium适用于以下浏览器:
Internet Explorer    Mozilla    Firefox    Safari
Windows XP    6.0    1.6+, 1.7+    0.8+, 0.9+, 1.0
Red Hat Linux       1.6+, 1.7+    0.8+, 0.9+, 1.0+   
Mac OS X 10.3    不支持    1.6+, 1.7+    0.8+, 0.9+, 1.0+    1.3+
二、命令
通过Selenium 命令,脚本编写者可以描述 browser bot 在浏览器中所执行的操作。
可以将这些命令分成两类 —— 操作(action) 和断言(assertion):
● 操作:模拟用户与 Web 应用程序的交互。
         如:单击一个按钮和填写一个表单,这些都是常见的用户操作,可以用 Selenium 命令来自动化这些操作。
● 断言:验证一个命令的预期结果。
         常见的断言包括验证页面内容或当前位置是否正确。
三、执行模式
1、可以按两种模式来使用 Selenium:test runner 和 driven。
2、这两种模式在复杂性和编写方式方面有所不同:
   driven 测试脚本编写起来往往要更复杂一些,因为它们是用编程语言编写的。
   但是如果使用 Python 或 Ruby 之类的高级动态编程语言,那么这种复杂性方面的差异就很小。
3、两种模式之间最大的不同点在于:
   如果使用 driven 脚本,测试有一部分在浏览器之外运行,而如果使用 test runner 脚本的话,测试是完全在浏览器中运行的。
4、不管是 test runner 还是 driven 测试用例,都可以与持续集成工具集成。
四、组成
●Selenium IDE:
   一个firefox的plug-in,可以录制和回放并保存test cases,测试用例为html格式。
  (如果不用FF插件,录制或编辑好的测试脚本需要在Core部署下进行回放)
●Selenium Core:
   整个测试机制的核心部分,即有assertion(断言)机制的test suite runner。
   它由一些纯js代码组成, 可以运行在windows/linux的不同browser上(相当于Jmeter 的runner 跟 Assertion)
●Selenium Remote Control:
   一个代理与控制端, 可代替Selenium core/ Selenium IDE的client端(相当于通过编程来实现一切),是支持多语言的.
   (相当于Jmeter的client/server模式,但Selenium Remote Control更强一些)
b) 环境配置
从官方网下载selenium包,解压到指定目录
c) 使用案例(启动selenium server执行测试脚本,形成测试报告,关闭seleniume server)
<!--===== selenium自动化测试 =============-->
<target name="start-seleniumserver">
<java jar="${selenium.root}/selenium-server-standalone-2[1].8.0.jar"
fork="true" spawn="true" />
</target>
<target name="stop-seleniumserver">
<get taskname="selenium-shutdown" src="http://localhost:4444/selenium-server/driver/?cmd=shutDown"
ignoreerrors="true" dest="result.txt"/>
<echo taskname="selenium-shutdown"
message=" Errors during shutdown are expected" />
</target>
<target name="auto-test" depends="compile,start-seleniumserver"
description="run junit test">
<mkdir dir="${report}/selenium/xml" />
<mkdir dir="${report}/selenium/html" />
<junit dir="." fork="true" printsummary="on"
haltonfailure="false" failureproperty="tests.failed"
showoutput="true">
<classpath refid="junit-classpath" />
<classpath location="${class.dir}/classes" />
<!-- 生成报告数据的格式,可能多个,支持xml/brief/plain -->
<formatter type="xml" />
<batchtest todir="${report}/selenium/xml">
<fileset dir="${class.dir}/classes">
<include name="**/OaSystemTestSuite.*" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${report}/selenium/html">
<fileset dir="${report}/selenium/xml">
<include name="*.xml" />
</fileset>
<report format="frames" todir="${report}/selenium/html" />
</junitreport>
</target>

Selenium学习网站:
http://www.openqa.org/
http://selenium.openqa.org/
http://www.selenium.cn

六、 Cobertura-单元测试覆盖率测试报告生成工具
a) 工具简介
cobertura 在西班牙语是覆盖的意思,Cobertura 是一种开源工具,它通过检测基本的代码,并观察在测试包运行时执行了哪些代码和没有执行哪些代码,来测量测试覆盖率。除了找出未测试到的代码并发现 bug 外,Cobertura 还可以通过标记无用的、执行不到的代码来优化代码。
cobertura 的基本工作思路就是:
(1) 对已经编译好的class 文件添加日志代码
(2) 对添加好日志的代码进行单元测试
(3) 输出覆盖率统计报告
统计报告是html 格式的,其中标出每行源码是否执行,非常直观。
所以我们在ant 中使用cobertura 的步骤就是:
(1) 编译代码
(2) 定义 cobertura 的ant task
(3) 用 cobertura-instrument 命令为编译好的代码添加日志
(4) 用 junit 命令对添加好日志的代码进行单元测试
(5) 用 cobertura-report 命令输出报告
b) 环增配置
从官方网下载cobertura解压到指定目录
c) 使用案例
<!--================= 检查代码测试覆盖率 ================-->
<property name="cobertura.dir" location="D:/cobertura-1.9.4.1" />
<property name="instrumented.dir" location="D:/instrumented" />
<path id="cobertura-classpath">
<fileset dir="${cobertura.dir}">
<include name="*.jar" />
<include name="**/*.jar" />
</fileset>
<fileset dir="${weblib.dir}">
<include name="*.jar" />
</fileset>
<fileset dir="${selenium.root}">
<include name="selenium-*.jar" />
</fileset>
</path>
<!-- taskdef name="instruments" classpathref="cobertura-classpath"
classname="net.sourceforge.cobertura.ant.InstrumentTask" /-->

<taskdef classpath="${cobertura.dir}/cobertura.jar"
resource="tasks.properties" />

<target name="instrument">
<delete dir="${instrumented.dir}" />
<mkdir dir="${instrumented.dir}" />
<cobertura-instrument todir="${instrumented.dir}/classes">
<classpath refid="cobertura-classpath" />
<fileset dir="${class.dir}/classes">
<include name="**/*.class" />
<exclude name="**/*Test.class" />
<exclude name="**/Test*.class" />
<exclude name="**/*TestSuite.class" />
</fileset>
</cobertura-instrument>
</target>

<target name="cover-test" depends="instrument">
<mkdir dir="${report}/cover" />
<junit dir="." failureproperty="test.failure" printsummary="on"
haltonfailure="false" fork="true" showoutput="true">
<classpath refid="cobertura-classpath" />
<classpath location="${instrument.dir}/classes" />
<batchtest todir="${report}/cover">
<fileset dir="${class.dir}/classes">
<include name="**/*Test.*" />
<exclude name="**/selenium/*Test.*" />
</fileset>
</batchtest>
</junit>
</target>

<target name="coverage-report" depends="compile,cover-test">
<cobertura-report format="html"
srcdir="${local.dir}/src/main/java" destdir="${report}/cover">
<classpath refid="cobertura-classpath" />
</cobertura-report>
</target>

七、 Hudson-代码持续集成工具
a) Hudson 简介
Hudson 是一种革命性的开放源码 CI 服务器,它从以前的 CI服务器吸取了许多经验 教训。Hudson 最吸引人的特性之一是它很容易配置:很难找到更容易设置的 CI 服务器,也很难找到开箱即用特性如此丰富的CI 服务器。Hudson 容易使用的第二个原因是它具有强大的插件框架 ,所以很容易添加特性。例如,一个 Hudson 插件可以随时间的推移跟踪FindBugs 和代码覆盖。它还可以报告测试结果的趋势(来自 JUnit 或 TestNG)以及构建结果和对应的执行时间。
Hudson 需要运行 Java 5。如果需要使用 Hudson 附带的嵌入式 容器(Winstone)之外的其他容器,那么只需使用一种 Servlet 2.4 容器。对于大多数情况,Winstone 就足够了。
b) Hudson使用
CI 过程的最后一个方面是 CI 服务器本身。CI服务器在整个开发过程中的主要作用是控制者:当服务器在代码存储库中探测到修改时,它将运行构建的任务委托给构建过程本身。如果构建失败了,那么 CI服务器将通知相关方面,然后继续监视存储库。它的角色看起来是被动的;但是,它是快速反映问题的关键。
安装 Hudson
使用 Hudson 的主要好处之一是它的设置很简单。在最简单的情况下,Hudson 只需要两个步骤:
下载最新的版本(它打包为一个 WAR 文件)。 hudson官方网址:https://hudson.dev.java.net/
运行java -jar hudson.war 。
这样就可以了。因为下载的是一个 WAR 文件,所以如果愿意,可以将它部署在 Tomcat 或 JBoss 等容器中。这完全由您自己决定。当然,Hudson 假设在安装它的机器上运行着 Java 5,而且如果定义了JAVA_HOME 环境变量,Hudson 就会使用它。(正如前面提到的,Hudson 需要 Java 5。)
在安装并运行 Hudson 之后(将 WAR 文件部署到 servlet 容器或从命令行执行java -jar hudson.war ),启动浏览器并访问默认安装位置。如果通过命令行运行 Hudson 而且您在本地机器上,那么可以访问http://localhost:8080/ 。
配置 Hudson 
参数说明:
system.message 填写一些说明信息
Quiet period:hudson定时构建工程的时间(秒)
Enable security:设置hudson登陆的规则(默认为匿名登陆)
TCP port for JNLP slave agents:不了解JNLP不敢胡写总之就是三种方式:固定(fixed) 随机(Radom) 不使用(disabled),使用固定时可以填入JNLP信息
security realm:可以使用中间件容器,数据库,LDAP来验证安全,具体怎样用法没用过,以后会有更新,研究中.
authorized:可以设置身份的验证方法:系统用户,匿名用户,自定义用户,还有继承用户(此处也在研究中,建议使用匿名用户)
JDK installations:设置JDK的安装路径
Shell executable:设置window shell命令
Ant installation:设置ant 的安装路径
mave installation设置mave的安装路径
cvs executable:设置cvsnt执行进程的路径(cvs.exe)
.cvspass file:设置cvsnt管理员文件的路径(passwd文件)
e-mail notification:设置当发生错误时发送的邮箱地址
hudson url:就是hudson的默认地址
还可以配置服务器的其他几个方面,比如向 Hudson 提供一个电子邮件服务器的位置,以便在构建失败时接收电子邮件。根据您的组织设置电子邮件的方式,可能需要让系统管理员帮助设置这个特性。设置电子邮件并不是必需的;Hudson 还支持以 RSS 作为通知机制,对于某些人来说,这种方式比电子邮件更好。究竟选择哪些通知机制完全取决于您。(注意,这里说的是 “哪些”,也就是说,可以同时使用多种通知机制!)
在 Hudson 中配置项目
既然 Hudson 已经能够与 SCM 存储库通信了,就该配置项目了。这个示例所用的项目称为 solar-ci。在 Hudson 主页上单击左上角的 New Job 链接。这时会看的屏幕: 
该页面可以使我们通过hudson来管理cvs里的一个对应的工程
Project name:工程名称
Description:描述信息
Discard build:如果选择此项可以设置build记录保存的天数,或者build记录保存的数理,或者只保存最新的build记录,一般不需填写
Advance project options:可以设置hudson定时检查cvs工程的时间间隔,还可以指定cvs工程check out到本地的工程路径,一般不需要填写
Source code management:我们选择cvs将出现以下参数:
Cvsroot:将写cvs登陆字符串,格式(:protocol:user:password@host:path),例如: :pserver:cvsadmin:[email protected]:2401:/CVSNT/Repository,使用cvs必填
Modules:填写cvs仓库下的具体工程名, 使用cvs必填
Branch:填写分支名称,也可以勾选this is a tag,no a branch指定标记名称选择subversion可以进行相应的subversion设置
Build trigger可以设置hudson自动执行的一些动作,build after others projects are built指定hudson构建完成后需要继续构建的工程名
Build periodically 根据hudson定义的语法规则来设定自动构建工程的时间间隔
Post-build actions
设置一些构建完成后的动作,如放邮件,打包,产生测试报告,产生java doc 等.
点击ok保存设置
使用hudson
进入刚配置的项目,可以在左侧build history看到历史的build记录,点击build now 可以手动执行构建动作,完成后可以通过记录标记的颜色来看是否出错,红色有错,蓝色成功.点击记录查看详细信息,如果有变化hudson将列出类信息
elipse插件应用
eclipse updatesite:http://code.google.com/p/hudson-eclipse/
重新打开eclipse在windows->preferences下将出现hudson选项,设置默认的hudson url保存.
然后选择windows->open view打开hudson view
如果你己经配置hudson项目将列出hudson的项目名称,右键菜单可以看到所有的执行菜单,使用还是很方便的,

八、 Xpath Checker
用于定位dom标签元素,提高开发人员编写selenium脚本的效率

九、 附件:
Build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="oa" default="checkout" basedir=".">

<!-- SVN服务器远程配置信息 -->
<property name="remote.url"
value="https://xx.xxx.xx.xx/svn/gxwj/oa" />
<!-- 本地源代码存放目录 -->
<property name="local.dir" value="D:/srccode2" />
<!-- 日志文件存放路径配置 -->
<property name="build.log.dir" location="D:/buildlogs" />
<!-- 测试报告文件存放目录 -->
<property name="report" location="d:/report" />
<!-- 编译文件存放目录 -->
<property name="class.dir" location="D:/classtemp" />
<!-- 正式打包文件 -->
<property name="des_project" location="D:/oa" />
<property name="src.dir" value="${local.dir}/src/main/java" />
<property name="src.test.dir" value="${local.dir}/src/test/java" />
<property name="resource.dir"
value="${local.dir}/src/main/resource" />
<property name="resource.test.dir"
value="${local.dir}/src/test/resource" />
<property name="weblib.dir" value="${local.dir}/webapp/WEB-INF/lib" />
<property name="selenium.root"
value="${local.dir}/build/lib/selenium" />
<property name="ant.root" value="${local.dir}/build/lib/ant" />

<property name="findbugs.dir" location="D:/findbugs-1.3.9" />
<path id="findbugs-classpath">
<fileset dir="${findbugs.dir}" includes="lib/*.jar" />
</path>
<taskdef name="findbugs"
classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
classpathref="findbugs-classpath" />


<!--=================== 类库文件配置信息 ==================-->
<path id="system-classpath">
<fileset dir="${ant.root}">
<include name="ant.jar" />
</fileset>
<fileset dir="${weblib.dir}">
<include name="*.jar" />
</fileset>
<fileset dir="${selenium.root}">
<include name="selenium-*.jar" />
</fileset>
</path>

<!--===================== svn版本控制器类 ====================-->
<taskdef name="svn"
classname="org.tigris.subversion.svnant.SvnTask" />

<!--================== 清理目录 ================-->
<target name="clean" description="Clean output dirs">
<!-- delete dir="${local.dir}" /> -->
<delete dir="${class.dir}" />
<delete dir="${report}" />
<mkdir dir="${local.dir}" />
<mkdir dir="${class.dir}/classes" />
</target>

<!--=============== 从SVN服务器下载源代码 ==================-->
<target name="checkout" depends="clean">
<svn javahl="true" username="xxx" password="xxx">
<checkout url="${remote.url}" destPath="${local.dir}" />
</svn>
</target>

<!--==================== 创建日志文件,文件名以日期格式命名 ==============-->
<target name="init">
<tstamp>
<format property="timestamp" pattern="yyyy-MM-dd_HH-mm-ss" />
</tstamp>
<mkdir dir="${build.log.dir}" />
<property name="build.log.filename"
value="build_${timestamp}.log" />
<record name="${build.log.dir}/${build.log.filename}"
loglevel="verbose" append="false" />
<echo message="Build logged to ${build.log.filename}" />
</target>

<!--====================== 编译 ===========================-->
<target name="compile"
description="Compile main source tree java files into class files">
<javac srcdir="${src.dir}:${src.test.dir}" encoding="utf-8"
destdir="${class.dir}/classes" source="1.6" target="1.6" debug="true"
deprecation="false" optimize="false" failonerror="true"
includeantruntime="on">
<classpath refid="system-classpath" />
</javac>
</target>

<!--===================== 构建代码 ======================-->
<target name="build" depends="compile">
<copy todir="${class.dir}/classes"
preservelastmodified="true">
<fileset dir="${resource.dir}">
<include name="**/*.xml" />
<include name="**/*.properties" />
</fileset>
<fileset dir="${resource.test.dir}">
<include name="**/*.xml" />
<include name="**/*.properties" />
</fileset>
</copy>

<mkdir dir="${des_project}" />

<copy todir="${des_project}" preservelastmodified="true">
<fileset dir="${local.dir}/webapp">
<include name="*" />
<include name="**/*.*" />
<!-- 排除以.svn为缀的文件 -->
<exclude name="**/*.svn" />
</fileset>
</copy>

<copy todir="${des_project}/WEB-INF/lib"
preservelastmodified="true">
<fileset dir="${ant.root}">
<include name="ant.jar" />
</fileset>
<fileset dir="${selenium.root}">
<include name="selenium-*.jar" />
</fileset>
</copy>

<move todir="${des_project}/WEB-INF">
<fileset dir="${class.dir}">
<exclude name="**/*Test.*" />
<exclude name="**/Test*.*" />
<exclude name="**/*TestSuite.*" />
</fileset>
</move>

<!-- 修改数据库配置文件内容 -->
<!--  replace dir="D:/oa/WEB-INF/classes"
includes="jdbc.properties">
<replacefilter token="testoa2" value="idcoa" />
<replacefilter token="123456" value="654321" />
</replace>-->
</target>

<!--========================= junit单元测试 =======================-->
<path id="junit-classpath">
<fileset dir="${weblib.dir}">
<include name="**/*.jar" />
</fileset>
<fileset dir="${selenium.root}">
<include name="selenium-*.jar" />
</fileset>
</path>
<target name="test" depends="compile"
description="run junit test">
<mkdir dir="${report}/junit/xml" />
<mkdir dir="${report}/junit/html" />
<junit dir="." fork="true" printsummary="on"
haltonfailure="false" failureproperty="tests.failed"
showoutput="true">
<classpath refid="junit-classpath" />
<classpath location="${class.dir}/classes" />
<!-- 生成报告数据的格式,支持xml/brief/plain -->
<formatter type="xml" />
<batchtest todir="${report}/junit/xml">
<fileset dir="${class.dir}/classes">
<include name="**/*Test.*" />
<include name="**/Test*.*" />
<exclude name="**/selenium/*.*" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${report}/junit/html">
<fileset dir="${report}/junit/xml">
<include name="*.xml" />
</fileset>
<report format="frames" todir="${report}/junit/html" />
</junitreport>
</target>

<!--================== 运用findbug工具检查代码bug ======================-->
<target name="findbug" depends="compile"
description="findbug工具检查代码bug,并生成bug报告">
<mkdir dir="${report}/findbug" />
<findbugs home="${findbugs.dir}"
excludefilter="${findbugs.dir}/findbug/findBugsexcludeFilter.xml"
output="html" outputFile="${report}/findbug/findbugs-report.html"
jvmargs="-Xmx512m" failonerror="true" reportlevel="low"
warningsproperty="bugs.found">
<!-- classes.dir是需要用findbugs检查的代码的class文件目录 -->
<class location="${class.dir}/classes" />
<!-- classpath refid="system-classpath" /> -->
<!--  以上定义上述类所依赖的类路径  -->
<!--  auxclasspath path="${findbugs.dir}/lib/findbugs-ant.jar" />-->
<!--  以上定义源代码的路径  -->
<sourcePath path="${local.dir}/src/main/java" />
</findbugs>
</target>


<!--================== 运用checkstyle检查代码编写规范 ======================-->
<property name="checkstyle.dir" location="D:/checkstyle-5.4" />

<path id="checkstyle-classpath">
<fileset dir="${checkstyle.dir}"
includes="checkstyle-5.4-all.jar" />
</path>

<taskdef name="checkstyle"
classname="com.puppycrawl.tools.checkstyle.CheckStyleTask"
classpathref="checkstyle-classpath" />

<target name="checkstyle" description="检查代码编写规范,并生成编码检查报告">
<mkdir dir="${report}/checkstyle" />
<checkstyle config="${checkstyle.dir}/sun_checks.xml"
failureProperty="checkstyle.failure" failOnViolation="false">
<formatter type="xml"
tofile="${report}/checkstyle/checkstyle_report.xml" />
<fileset dir="${local.dir}/src" includes="**/*.java" />
</checkstyle>
<style in="${report}/checkstyle/checkstyle_report.xml"
out="${report}/checkstyle/checkstyle_report.html"
style="${checkstyle.dir}/contrib/checkstyle-frames.xsl" />
</target>


<!--================= 检查代码测试覆盖率 ================-->
<property name="cobertura.dir" location="D:/cobertura-1.9.4.1" />
<property name="instrumented.dir" location="D:/instrumented" />
<path id="cobertura-classpath">
<fileset dir="${cobertura.dir}">
<include name="*.jar" />
<include name="**/*.jar" />
</fileset>
<fileset dir="${weblib.dir}">
<include name="*.jar" />
</fileset>
<fileset dir="${selenium.root}">
<include name="selenium-*.jar" />
</fileset>
</path>
<!-- taskdef name="instruments" classpathref="cobertura-classpath"
classname="net.sourceforge.cobertura.ant.InstrumentTask" /-->

<taskdef classpath="${cobertura.dir}/cobertura.jar"
resource="tasks.properties" />

<target name="instrument">
<delete dir="${instrumented.dir}" />
<mkdir dir="${instrumented.dir}" />
<cobertura-instrument todir="${instrumented.dir}/classes">
<classpath refid="cobertura-classpath" />
<fileset dir="${class.dir}/classes">
<include name="**/*.class" />
<exclude name="**/*Test.class" />
<exclude name="**/Test*.class" />
<exclude name="**/*TestSuite.class" />
</fileset>
</cobertura-instrument>
</target>

<target name="cover-test" depends="instrument">
<mkdir dir="${report}/cover" />
<junit dir="." failureproperty="test.failure" printsummary="on"
haltonfailure="false" fork="true" showoutput="true">
<classpath refid="cobertura-classpath" />
<classpath location="${instrument.dir}/classes" />
<batchtest todir="${report}/cover">
<fileset dir="${class.dir}/classes">
<include name="**/*Test.*" />
<exclude name="**/selenium/*Test.*" />
</fileset>
</batchtest>
</junit>
</target>

<target name="coverage-report" depends="compile,cover-test">
<cobertura-report format="html"
srcdir="${local.dir}/src/main/java" destdir="${report}/cover">
<classpath refid="cobertura-classpath" />
</cobertura-report>
</target>


<!--================= 打成war包 ======================-->
<target name="war" depends="build"
description="Build the web application archive">
<war warfile="${des_project}.war" basedir="${des_project}"
webxml="${des_project}/WEB-INF/web.xml">
</war>
</target>

<property name="tomcat.home" value="D:/apache-tomcat-6.0.32" />
<property name="war.name" value="oa" />
<property name="war.home" value="D:/" />
<!--==========================启动tomcat=========================-->
<target name="tomcat.start" depends="tomcat.stop">
<delete dir="${tomcat.home}/webapps/oa" />
<delete file="${tomcat.home}/webapps/oa.war" />

<move todir="${tomcat.home}/webapps">
<fileset dir="${war.home}">
<include name="${war.name}.war" />
</fileset>
</move>
<java jar="${tomcat.home}/bin/bootstrap.jar" fork="true">
<jvmarg value="-Dcatalina.home=${tomcat.home}" />
</java>
</target>

<!--============================停止tomcat=================================-->
<target name="tomcat.stop">
<java jar="${tomcat.home}/bin/bootstrap.jar" fork="true">
<jvmarg value="-Dcatalina.home=${tomcat.home}" />
<arg line="stop" />
</java>
</target>


<!--========================== selenium自动化测试 ========================-->
<target name="start-seleniumserver">
<java
jar="${selenium.root}/selenium-server-standalone-2[1].8.0.jar"
fork="true" spawn="true" />
</target>

<target name="stop-seleniumserver">
<get taskname="selenium-shutdown"
src="http://localhost:4444/selenium-server/driver/?cmd=shutDown"
ignoreerrors="true" dest="result.txt"/>
<echo taskname="selenium-shutdown"
message=" Errors during shutdown are expected" />
</target>

<target name="auto-test" depends="compile,start-seleniumserver"
description="run junit test">
<mkdir dir="${report}/selenium/xml" />
<mkdir dir="${report}/selenium/html" />
<junit dir="." fork="true" printsummary="on"
haltonfailure="false" failureproperty="tests.failed"
showoutput="true">
<classpath refid="junit-classpath" />
<classpath location="${class.dir}/classes" />
<!-- 生成报告数据的格式,可能多个,支持xml/brief/plain -->
<formatter type="xml" />
<batchtest todir="${report}/selenium/xml">
<fileset dir="${class.dir}/classes">
<include name="**/OaSystemTestSuite.*" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${report}/selenium/html">
<fileset dir="${report}/selenium/xml">
<include name="*.xml" />
</fileset>
<report format="frames" todir="${report}/selenium/html" />
</junitreport>
</target>

<!--================ 打成zip包 =======================-->
<target name="zip" depends="build">
<zip destfile="${des_project}.zip">
<zipfileset dir="${des_project}" prefix="oa" />
</zip>
</target>

<!--=================清空目录==============-->
<target name="destory">
<delete dir="${class.dir}" />
<delete dir="${des_project}" />
<delete dir="${local.dir}" />
</target>

<!--========================== 关闭计算机 ========================-->
<target name="shutDown">
<sleep minutes="2" />
<exec executable="shutdown">
<arg value="-s" />
</exec>
</target>

<target name="all"
depends="clean,init,findbug,checkstyle,coverage-report,auto-test,war,zip,destory" />

</project>

猜你喜欢

转载自xuzhfa123.iteye.com/blog/1197728