2008年5月5日星期一

使用NetBeans与Ant实现Web项目自动集成测试


       NetBeans创建Web项目缺省是以Ant为基础进行构建,可以自动生成基本的编译,打包等Ant脚本,省去了手工编辑的繁琐,另外预留了一些Ant任务可供覆写和扩展,本文的目的是在NetBeans生成的Ant脚本基础上进行扩展,以实现一个完整的自动构建集成测试过程。
一、环境准备
  1.   NetBeans 6.1

  2.   测试所需的jar包:
    asm-2.2.1.jar
    asm-tree-2.2.1.jar
    cobertura.jar
    commons-codec-1.3.jar
    commons-collections.jar
    commons-httpclient-3.1.jar
    commons-io-1.3.1.jar
    commons-lang-2.3.jar
    cssparser-0.9.4.jar
    dom4j-1.6.1.jar
    hamcrest-core-1.1.jar
    hamcrest-library-1.1.jar
    htmlunit-1.14.jar
    jaxen-1.1.1.jar
    jdom-1.0.jar
    jmock-2.4.0.jar
    js-1.6R7.jar
    junit-3.8.2.jar
    jwebunit-core-1.4.1.jar
    jwebunit-htmlunit-plugin-1.4.1.jar
    nekohtml-0.9.5.jar
    pmd-4.2.1.jar
    regexp-1.3.jar
    servlet-api.jar
    strutstest-2.1.4.jar
    xercesImpl-2.6.2.jar
    xml-apis-1.3.02.jar
    xmlParserAPIs-2.6.2.jar
    xom-1.0.jar

  3. Ant 1.7.0,此外需要activation.jar,commons-net-1.4.1.jar,jakarta-oro-2.0.8.jar,mail.jar。将以上jar包拷贝到ANT_HOME/lib目录下,设置路径加入ANT_HOME/bin,还有环境变量ANT_OPTS=-Xms512m -Xmx1024m

  4. 一台远程部署服务器,安装Tomcat 6.0.16和Apache Httpd 2.2.8

  5. 在NetBeans中创建一个Web工程,使用strutsTest和JMock编写单元测试,JWebUnit编写端对端测试,编译打包通过。
二、在build.xml中添加Ant脚本
  1. 远程部署
        <property file="build.properties" />
        <!-- 覆写-post-dist任务,加构建版本号和时间 -->
        <target name="-post-dist" description="Record build information">
            <propertyfile file="build.info">
                <entry default="0001" key="build.number" operation="+" pattern="0000" type="int" />
                <entry default="now" key="build.time" pattern="yyyy.MM.dd HH:mm:ss" type="date" />
            </propertyfile>
            <copy file="build.info" tofile="${dist.dir}/build.info" />
            <ftp server="${ftp.server}"
                 remotedir="${ftp.remotedir}"
                 userid="${ftp.user}"
                 password="${ftp.password}"
                 depends="true"
                 binary="true"
                 verbose="true"
                 ignorenoncriticalerrors="true">
                <fileset dir="${dist.dir}" />
            </ftp>
        </target>
        <!-- 取消远程部署的旧版本应用 -->
        <target name="remove-remote-app" >
            <property name="status.file" location="deploy-${tomcat.server}.txt" />
            <get src="${tomcat.manager.url}/remove?path=/${webapp.name}"
                 dest="${status.file}"
                 username="${tomcat.username}"
                 password="${tomcat.password}" />
            <loadfile property="deploy.result" srcfile="${status.file}" />
            <echo>${deploy.result}</echo>
        </target>
        <!-- 部署新版本 -->
        <target name="deploy-remote-server" depends="clean,dist,remove-remote-app">
            <property name="install.file" location="deploy-remote-install.txt" />
            <property name="redist.url" value="file://${target.dir}" />
            <property name="target.url" value="path=/${webapp.name}&amp;war=${redist.url}" />
            <get src="${tomcat.manager.url}/install?${target.url}"
                 dest="${install.file}"
                 username="${tomcat.username}"
                 password="${tomcat.password}" />
            <loadfile property="deploy.remote.result" srcfile="${install.file}" />
            <echo>${deploy.remote.result}</echo>
        </target>

    build.properties为新建的资源文件,内容如下:
    reports.dir=build/test/results
    reports.xml.dir=${reports.dir}
    reports.html.dir=${reports.dir}/junit-html
    coverage.xml.dir=${reports.dir}/cobertura-xml
    coverage.html.dir=${reports.dir}/cobertura-html
    pmd.html.dir=${reports.dir}/pmd-html
    instrumented.dir=${reports.dir}/cobertura-inmt

    smtp.hostname=10.100.1.16
    smtp.hostport=25
    mail.user=xxxx
    mail.password=xxxx
    smtp.from=xxxx@xxxx.cn
    smtp.tolist=xxxx@xxxx.cn

    ftp.server=10.100.1.156
    ftp.remotedir=dist
    ftp.reportdir.junit=/home/httpd/html/htdocs/hosptest/junitreport
    ftp.reportdir.cobertura=/home/httpd/html/htdocs/hosptest/coberturareport
    ftp.reportdir.pmd=/home/httpd/html/htdocs/hosptest/pmdreport
    ftp.user=deployer
    ftp.password=deployer

    tomcat.server=10.100.1.156
    tomcat.manager.url=http://${tomcat.server}:8080/manager
    tomcat.username=system
    tomcat.password=manager
    webapp.name=hosp
    target.dir=/home/deployer/dist/hosp.war
    report.url=http://10.100.1.156/hosptest/

  2. 运行测试,生成JUnit单元测试报告,Cobertura测试覆盖率报告及PMD代码质量检查报告
        <!-- 装入Cobertura和PMD插件 -->
        <path id="cobertura_classpath">
            <fileset dir="lib">
                <include name="test/cobertura.jar" />
                <include name="test/asm-2.2.1.jar" />
                <include name="test/asm-tree-2.2.1.jar" />
                <include name="jakarta-oro-2.0.8.jar" />
                <include name="log4j-1.2.14.jar" />
            </fileset>
        </path>
        <taskdef classpathref="cobertura_classpath" resource="tasks.properties"/>
        <path id="pmd.classpath">
            <fileset dir="lib">
                <include name="test/pmd-4.2.1.jar" />
                <include name="test/asm-2.2.1.jar" />
                <include name="test/jaxen-1.1.1.jar" />
            </fileset>
        </path>
        <taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" classpathref="pmd.classpath"/>
        <target name="instrument" depends="init,compile">
            <delete file="cobertura.ser"/>
            <delete dir="${instrumented.dir}" />
            <cobertura-instrument todir="${instrumented.dir}">
                <ignore regex="org.apache.log4j.*" />
                <fileset dir="${build.classes.dir}">
                    <include name="**/*.class" />
                    <exclude name="**/*Test.class" />
                </fileset>
            </cobertura-instrument>
        </target>
        <target name="-pre-test-run" depends="init" if="have.tests">
            <mkdir dir="${reports.dir}"/>
            <mkdir dir="${instrumented.dir}" />
            <mkdir dir="${reports.html.dir}" />
            <mkdir dir="${coverage.xml.dir}" />
            <mkdir dir="${coverage.html.dir}" />
            <mkdir dir="${pmd.html.dir}" />
        </target>
       
        <target name="coverage-test" depends="init,-pre-test-run,compile-test">
            <junit dir="${basedir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" jvm="${platform.java}" showoutput="true">
                <classpath location="${instrumented.dir}" />
                <classpath location="${build.test.classes.dir}" />
                <classpath location="${build.classes.dir}" />
                <classpath path="${javac.test.classpath}" />
                <formatter type="xml" />
                <test name="${testcase}" todir="${reports.xml.dir}" if="testcase" />
                <batchtest todir="${reports.xml.dir}" unless="testcase">
                    <fileset dir="${test.src.dir}">
                        <include name="**/*Test.java" />
                        <exclude name="com/xued/hosp/jwebunit/*Test.java" />
                    </fileset>
                    <fileset dir="${test.src.dir}">
                        <include name="com/xued/hosp/jwebunit/*Test.java" />
                    </fileset>
                </batchtest>
            </junit>
            <junitreport todir="${reports.xml.dir}">
                <fileset dir="${reports.xml.dir}">
                    <include name="TEST-*.xml" />
                </fileset>
                <report format="frames" todir="${reports.html.dir}" />
            </junitreport>
        </target>
        <target name="coverage-check">
            <cobertura-check branchrate="34" totallinerate="100" />
        </target>
        <target name="coverage-report">
            <cobertura-report srcdir="${src.dir}" destdir="${coverage.xml.dir}" format="xml" />
        </target>
        <target name="alternate-coverage-report">
            <cobertura-report destdir="${coverage.html.dir}">
                <fileset dir="${src.dir}">
                    <include name="**/*.java"/>
                </fileset>
            </cobertura-report>
        </target>
        <!-- 运行所有测试,包括JUnit单元测试和JWebUnit端对端测试,生成测试报告 -->
        <target name="coverage" depends="instrument,coverage-test,coverage-report,alternate-coverage-report" />
        <!-- 运行PMD,生成代码质量报告 -->
        <target name="pmd">
            <pmd shortFilenames="true" classpath="." rulesetfiles="conf/pmd.xml">
                <formatter type="html" toFile="${pmd.html.dir}/index.html" linkPrefix="http://pmd.sourceforge.net/xref/"/>
                <fileset dir="src/java">
                    <include name="**/*.java"/>
                </fileset>
            </pmd>
        </target>

    pmd.xml指定了代码检查的规则集:
    <?xml version="1.0" encoding="UTF-8"?>
    <ruleset name="Custom ruleset"
        xmlns="http://pmd.sf.net/ruleset/1.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
        xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
        <rule ref="rulesets/basic.xml"/>
        <rule ref="rulesets/braces.xml"/>
        <rule ref="rulesets/controversial.xml">
            <exclude name="OnlyOneReturn" />
            <exclude name="DataflowAnomalyAnalysis" />
            <exclude name="AvoidFinalLocalVariable" />
        </rule>
        <rule ref="rulesets/codesize.xml/ExcessiveMethodLength"/>
        <rule ref="rulesets/codesize.xml/ExcessiveParameterList"/>
        <rule ref="rulesets/codesize.xml/CyclomaticComplexity" />
        <rule ref="rulesets/design.xml">
            <exclude name="ConfusingTernary" />
            <exclude name="UncommentedEmptyConstructor" />
        </rule>
        <rule ref="rulesets/imports.xml"/>
        <rule ref="rulesets/naming.xml"/>
        <rule ref="rulesets/optimizations.xml">
            <exclude name="MethodArgumentCouldBeFinal" />
            <exclude name="LocalVariableCouldBeFinal" />
            <exclude name="UseStringBufferForStringAppends" />
        </rule>
        <rule ref="rulesets/strictexception.xml">
            <exclude name="SignatureDeclareThrowsException" />
        </rule>
        <rule ref="rulesets/strings.xml"/>
        <rule ref="rulesets/typeresolution.xml">
            <exclude name="SignatureDeclareThrowsException" />
        </rule>
        <rule ref="rulesets/unusedcode.xml"/>
    </ruleset>

  3. 将测试报告上传,并且发送Email
        <!-- 删除旧的远程文件 -->
        <target name="clean-remote-report">
            <ftp server="${ftp.server}"
                 remotedir="${ftp.reportdir.junit}"
                 userid="${ftp.user}"
                 password="${ftp.password}"
                 action="del"
                 depends="true"
                 binary="true"
                 verbose="true"
                 ignorenoncriticalerrors="true">
                <fileset includes="**/*" />
            </ftp>
            <ftp server="${ftp.server}"
                 remotedir="${ftp.reportdir.cobertura}"
                 userid="${ftp.user}"
                 password="${ftp.password}"
                 action="del"
                 depends="true"
                 binary="true"
                 verbose="true"
                 ignorenoncriticalerrors="true">
                <fileset includes="**/*" />
            </ftp>
            <ftp server="${ftp.server}"
                 remotedir="${ftp.reportdir.pmd}"
                 userid="${ftp.user}"
                 password="${ftp.password}"
                 action="del"
                 depends="true"
                 binary="true"
                 verbose="true"
                 ignorenoncriticalerrors="true">
                <fileset includes="**/*" />
            </ftp>
        </target>
        <!-- 上传新的远程文件 -->
        <target name="ftp-report" depends="clean-remote-report">
            <ftp server="${ftp.server}"
                 remotedir="${ftp.reportdir.junit}"
                 userid="${ftp.user}"
                 password="${ftp.password}"
                 depends="true"
                 binary="true"
                 verbose="true"
                 ignorenoncriticalerrors="true">
                <fileset dir="${reports.html.dir}" />
            </ftp>
            <ftp server="${ftp.server}"
                 remotedir="${ftp.reportdir.cobertura}"
                 userid="${ftp.user}"
                 password="${ftp.password}"
                 depends="true"
                 binary="true"
                 verbose="true"
                 ignorenoncriticalerrors="true">
                <fileset dir="${coverage.html.dir}" />
            </ftp>
            <ftp server="${ftp.server}"
                 remotedir="${ftp.reportdir.pmd}"
                 userid="${ftp.user}"
                 password="${ftp.password}"
                 depends="true"
                 binary="true"
                 verbose="true"
                 ignorenoncriticalerrors="true">
                <fileset dir="${pmd.html.dir}" />
            </ftp>
        </target>
        <!-- 发送Email -->
        <target name="notifyteam" depends="ftp-report">
            <property file="build.info" />
            <mail mailhost="${smtp.hostname}"
                  mailport="${smtp.hostport}"
                  user="${mail.user}"
                  password="${mail.password}"
                  subject="Test build #${build.number}"
                  from="${smtp.from}" tolist="${smtp.tolist}" messagemimetype="text/html">
                <message>The build #${build.number} is now available.Please browse ${report.url} to see reports</message>
            </mail>
        </target>

  4. 将所有任务集成
    <target name="intergrate" depends="deploy-remote-server,coverage,pmd,notifyteam"/>

  5. 在命令行下输入ant intergrate即可完成所有任务,将此命令定制成自动执行脚本,即可实现自动集成。
  注意:以上步骤未包括最初从代码库中获取最新的代码,实际使用时可自行加入CVS或SVN任务实现这一步骤。

没有评论: