日历

« 2008-10-16  
   1234
567891011
12131415161718
19202122232425
262728293031 

最新来客

统计信息

  • 访问量: 1348
  • 日志数: 12
  • 建立时间: 2007-10-10
  • 更新时间: 2008-06-26

RSS订阅

Be smart ,be profession !

我的最新日志

  • 【转】EMMA:测试覆盖率工具(2)

    2008-6-26

    本文主要通过一个示例项目介绍如何在集成了 Ant 和 Junit 的基础上,利用 EMMA 来收集单元测试对代码的覆盖率。

    介绍测试代码覆盖率的重要性

    测试驱动开发(TDD)是极限编程的一个重要特点,它具有很多优点,并被越来越多的开发人员所接受。在测试驱动开发过程中,程序员经历了编写测试用例,实现功能,重构代码这个不断迭代的过程。实践证明,这个过程能显著提高我们的生产效率,并产生高质量的代码。它还能给我们以自信,让我们放心的重构自己的代码。

    测试代码确实能够保证代码的质量,但如果你以为自己已经写了一堆测试用例,并都能运行通过时,就能高枕无忧了,那么你错了。隐藏的 Bug 也许只是在等待时机让你的系统崩溃。这是什么原因呢?聪明的你肯定已经想到,测试代码是用来保证功能代码的质量的,但测试代码的质量如何,我们不得而知。我们需要知道,我们辛苦编写的测试代码到底覆盖了多少功能代码,这就是我写这篇文章的出发点,我将介绍一种测试代码覆盖率的工具 - EMMA。


    介绍 EMMA

    EMMA 是一个用于检测和报告 JAVA 代码覆盖率的开源工具。它不但能很好的用于小型项目,很方便得得出覆盖率报告,而且适用于大型企业级别的项目。

    EMMA 有许多优点,首先你能免费得到它,并把它用于自己项目的开发。它支持许多种级别的覆盖率指标:包,类,方法,语句块(basic block)和行,特别是它能测出某一行是否只是被部分覆盖,如条件语句短路的情况。它能生成 text,xml,html 等形式的报告,以满足不同的需求,其 html 报告提供下钻功能,我们能够从 package 开始一步步链接到我们所关注的某个方法。EMMA 能和 Makefile 和 Ant 集成,便于应用于大型项目。特别还须指出的一点是,EMMA 的效率很高,这对于大型项目来说很重要。

    EMMA 是通过向 .class 文件中插入字节码的方式来跟踪记录被运行代码信息的。EMMA 支持两种模式:On the fly 和 Offline 模式。

    On the fly 模式往加载的类中加入字节码,相当于用 EMMA 实现的 application class loader 替代原来的 application class loader。

    Offline 模式在类被加载前,加入字节码。

    On the fly 模式比较方便,缺点也比较明显,如它不能为被 boot class loader 加载的类生成覆盖率报告,也不能为像 J2EE 容器那种自己有独特 class loader 的类生成覆盖率报告。这时,我们能求助于 Offline 模式。

    EMMA 也支持两种运行方式:Command line 和 Ant。

    本文后面提供的实例主要是演示如何集成 EMMA 和 Ant,通过 Offline 模式产生覆盖率报告。

    示例工程 SampleProject 是个小型的项目,有一个类 NumberParser,主要功能是把一个字符串解析成 float 型。下面是整个工程的目录结构。

    图1. 示例项目的目录结构
    图1. 示例项目的目录结构 

    下面,我们开始来为我们的工程编写 Ant 脚本。


    清单1设置一些属性,包括源文件,二进制文件,JUnit 报告,覆盖率报告等的路径
                <!-设置Java类被注入字节码后存放的路径-->
                <property name="bin.instrument.dir" location="../instrbin" />
                <!-设置覆盖率元数据和报告的路径-->
                <property name="coverage.dir" location="../coverage" />
                <!--设置junit报告的路径 -->
                <property name="junitReport.dir" location="../junitReport" />
                <!-设置主题代码bin路径-->
                <property name="bin.main.dir" location="../srcbin" />
                <!-设置测试代码bin路径-->
                <property name="bin.test.dir" location="../testbin" />
                <!--设置主题代码源路径-->
                <property name="src.main.dir" location="../../SampleProject/src" />
                <!--设置测试代码源路径-->
                <property name="src.test.dir" location="../../SampleProjectTest/test"
                />
                <!-指示需要注入字节码的Java类的路径-->
                <path id="classpath.main">
                <pathelement location="${bin.main.dir}" />
                </path>
                <!-指示 emma.jar 和emma_ant.jar 的路径-->
                <path id="emma.lib">
                <pathelement location="${libs}/emma.jar" />
                <pathelement location="${libs}/emma_ant.jar" />
                </path>
                <!-允许emma-->
                <property name="emma.enabled" value="true" />
                

    其中目录${ bin.instrument.dir }存放被注入字节码的类,"emma.lib" 指向 emma 资源所在的位置。
    清单2为 ANT 定义 EMMA 任务

    	<!-为ANT添加EMMA任务-->
                <taskdef resource="emma_ant.properties" classpathref="emma.lib" />
                

    清单3编译源代码和测试代码
    		<target name="compile-src.main">
                <mkdir dir="${bin.main.dir}" />
                <javac destdir="${bin.main.dir}" debug="on">
                <src path="${src.main.dir}" />
                </javac>
                <copy todir="${bin.main.dir}">
                <fileset dir="${src.main.dir}">
                <exclude name="**/*.java" />
                </fileset>
                </copy>
                </target>
                <target name="compile-src.test">
                <mkdir dir="${bin.test.dir}" />
                <javac destdir="${bin.test.dir}" debug="on">
                <src path="${src.test.dir}" />
                <classpath location="${bin.main.dir}" />
                </javac>
                <copy todir="${bin.test.dir}">
                <fileset dir="${src.test.dir}">
                <exclude name="**/*.java" />
                </fileset>
                </copy>
                </target>
                

    编译分两阶段,先编译源代码,然后再编译测试用例代码。

    清单4在所要测试类的代码中插入字节码
    		<!-对编译在路径bin.main.dir中的Java类注入字节码,
                并且把注入字节码的新Java类存放到路径bin.instrument.dir-->
                <!-覆盖率的元数据存放在路径coverage.dir中-->
                <target name="instrument">
                <mkdir dir="${bin.instrument.dir}" />
                <mkdir dir="${coverage.dir}" />
                <emma enabled="${emma.enabled}">
                <instr instrpathref="classpath.main"
                destdir="${bin.instrument.dir}"
                metadatafile="${coverage.dir}/metadata.emma"
                merge="true">
                </instr>
                </emma>
                <copy todir="${bin.instrument.dir}">
                <fileset dir="${bin.main.dir}">
                <exclude name="**/*.java" />
                </fileset>
                </copy>
                </target>
                

    当${emma.enabled}为 true 时,才生成插入字节码的类。<instr>中指定了要 instrument 的类的地址, instrumented 后类存放的地址,以及 metadata 存放的地址。
    清单5运行测试用例,得到一些生成报告的元数据

                <!-执行测试用例同时生成junit测试报告和emma代码覆盖率报告-->
                <target name="test">
                <mkdir dir="${junitReport.dir}" />
                <junit fork="true" forkmode="once"
                printsummary="withOutAndErr"
                errorproperty="test.error"
                showoutput="on">
                <!-指明代码覆盖率的元数据的存放位置-->
                <jvmarg
                value="-Demma.coverage.out.file=${coverage.dir}/metadata.emma" />
                <jvmarg value="-Demma.coverage.out.merge=true" />
                <classpath location="${bin.instrument.dir}" />
                <classpath location="${bin.test.dir}" />
                <classpath refid="emma.lib" />
                <formatter type="xml" />
                <!-执行所有以Test结尾的junit测试用例-->
                <batchtest todir="${junitReport.dir}" haltonfailure="no">
                <fileset dir="${bin.test.dir}">
                <include name="**/*Test.class" />
                </fileset>
                </batchtest>
                </junit>
                </target>
                

    在运行测试用例前,需要设置 jvmarg。所有的测试用例都跑在 instrumented 的类上面。
    清单6生成 JUnit 报告
     

                <target name="gen-report-junit">
                <!-生成junit测试报告-->
                <junitreport todir="${junitReport.dir}">
                <fileset dir="${junitReport.dir}">
                <include name="*" />
                </fileset>
                <report format="frames" todir="${junitReport.dir}" />
                </junitreport>
                </target>
                

     清单7生成覆盖率报告
    		<!-生成代码覆盖率报告-->
                <target name="gen-report-coverage">
                <!-如果属性emma.enabled的值是true,就生成代码覆盖率报告 -->
                <emma enabled="${emma.enabled}">
                <report sourcepath="${src.main.dir}"
                sort="+block,+name,+method,+class"
                metrics="method:70,block:80,line:80,class:100">
                <fileset dir="${coverage.dir}">
                <include name="*.emma" />
                </fileset>
                <html ōutfile="${coverage.dir}/coverage.html"
                depth="method" columns="name,class,method,block,line" />
                </report>
                </emma>
                </target>
                

    指明源代码所在的位置,以便能够显示每行代码的覆盖情况。Sort指明生成列表的排列顺序,"+"表示升序,"-"表示降序。Metrics 可为每个度量指明一个覆盖率阈值,若未达到该阈值,则该行会被标记出来(前提是报告的形式支持这个功能,如 HTML)。<html>指明以 HTML 形式生成报告,Depth 指明报告的详细程度,columns 指明生成列表列名的排列顺序。


    显示报告

    我们已经写好了Ant脚本,接下来你就可以运行该脚本了。这里假设你已经搭好了运行 Ant 和 JUnit 的环境,直接到脚本所在目录,在命令行敲入 Ant 即可。
    图2整个项目层次的报告
    图2整个项目层次的报告 
    图3包层次的报告
    图3包层次的报告
    图4类层次的报告
    图4类层次的报告 图5用颜色标记的源代码

    你会发现有三种颜色,绿色,红色和黄色,它们分别表示该行:被测试到,未被测试到,以及部分被测试到。红色或黄色的部分是需要引起你注意的,bug 也许就隐藏在这部分代码中,你所需做的就是设计一些测试用例,使它们运行以前未被执行到的语句。如上面那张图给出了我们一些信息,String 中含有"+"号的情况未被测试到,还有"isPositive"只被测试到 true 或 false 的一种情况,你需要相应的增加一些测试用例。运行新加的测试用例,你也许会发现一些新的 bug,并修正这些 bug。TestAge 中国软件测试时代

    藏在报告背后的问题

    对于这个简单的例子,你会发现,我们很容易达到 100% 的测试覆盖率,你也许会松口气说:啊,我把所有情况都测试到了,这下放心了。在这里很遗憾的告诉你,EMMA 的功能是有限的,它不支持决策覆盖和路径覆盖。事实上,对于一个稍复杂的工程进行穷尽的测试是不可能的。


    清单8决策覆盖和路径覆盖的代码示例
                /**
                * Parses the given string to a float number
                *
                * @param number
                *            the given string
                * @return the float number related with the string
                *
                * @throws IllegalArgumentException
                *             if the string is empty, null or can not parse to a float
                */
                public float parse(String number) {
                if (number.equals("")||number == null ) {
                throw new IllegalArgumentException(
                "Number string should not be empty or null");
                }
                StringIterator stringIterator = new StringIterator(number);
                getSign(stringIterator);
                int integer = getInteger(stringIterator);
                float fraction = getFraction(stringIterator);
                float total = integer + fraction;
                return isPositive ? total : (-1) * total;
                }
                

     清单9决策覆盖和路径覆盖的测试用例
                public void test_parse () {
                NumberParser np = new NumberParser();
                String number ="";
                try {
                np.parse(number);
                fail("should throw IAE");
                } catch (IllegalArgumentException e) {
                // pass
                }
                number = "22.010";
                float parsedNumber = np.parse(number);
                assertEquals((float) 22.010, parsedNumber);
                number = "-22.010";
                parsedNumber = np.parse(number);
                assertEquals((float) 22.010, parsedNumber);
                }
                

    运行 Ant 脚本,生成报告,你会发现,测试用例都运行通过了,测试覆盖报告也表明代码所有的行都被执行到了。但细心的读者肯定早已看到上面代码存在 Bug。若传进 parse 的 string 为 null 的话,并不是如我们所愿,得到 IllegalArgumentException,而是抛出了 NullPointerException。

    虽然下面那行是绿色的,但它只表明每个条件语句都被执行到了,并不能说明每个条件都取到true和false两种情况。在我们设计的测试用例中,"null == number"只取到 false 一种情况。我们需要在我们的测试用例中加入对 string 情况是 null 的测试。


    图6 决策覆盖和路径覆盖率报告
    图6 决策覆盖和路径覆盖率报告 
    清单10 修正代码的 Bug
     
                    if (null == number || "".equals(number)) {
                

    为你的项目生成覆盖率报告,EMMA 是个不错的选择。通过覆盖率报告,我们能发现并修复一些隐藏的 bug,我们的软件会变得更强壮。

  • 【转】EMMA:测试覆盖率工具(1)

    2008-6-26

    以前在做过的一个项目上面应用过EMMA,不过当时不是很清楚原理,今天又重新学习了一下。找了两篇不错的帖子跟大家共享一下,呵呵。

    在讨论EMMA的使用之前,我首先简要介绍几个相关的概念。

     测试覆盖率(Code Coverage)
      测试覆盖率,简单的说,就是评价测试活动覆盖产品代码的指标。测试的目的,是确认产品代码按照预期一样工作,也可以看作是产品代码工作方式的说明文档。进一步考虑,测试覆盖率可以看作是产品代码质量的间接指标--之所以说是间接指标,因为测试覆盖率评价的是测试代码的质量,并不是产品代码的质量。

     代码覆盖率是一种白盒测试,因为测试覆盖率是评价产品代码类内部的指标,而不是评价系统接口或规约。测试覆盖率尤其用于评价测试代码是否已经覆盖了产品代码所有的路径。

     衡量测试覆盖率的指标很多,常用的指标有:

     Statement coverage,也称作Line coverage,用于评价测试的代码语句覆盖率。

     Basic block coverage,是Statement coverage的一个变种,它把没有一个分支的代码区域作为一个计量单位,而不是简单的代码行,用于一个if-else分支代码行数远远大于另一个的情况,在这种情况下,statement coverage指标并不适用。

     Decision coverage(也称作Branch coverage),用于评价代码分支地测试覆盖率。

     Path coverage,和Decision coverage相似,用于评价代码从开始到结束所有路径的测试覆盖率。

     Function coverage,用于评价代码方法的测试覆盖率。

     EMMA目前支持四种Coverage类型:class、method、line和basic block。

     测试覆盖率的实现方式

      实现测试服务覆盖率的技术通常分为两种:

     1、Instrumentation

     Instrumentation技术在产品代码的关键位置插入统计代码。事实上,Instrumentation技术可以分为两种方式:Class Instrumentation和Source Instrumentation。前者把统计代码插入编译好的.class文件,而后者则把统计代码插入源代码并编译成新的.class文件。大多数测试覆盖率工具采用这两种Instrumentation技术。

       2、Custom JVM

     另一种方式是在JVM中把统计代码插入.class。测试覆盖率分析可以在JVM执行测试代码的过程中完成。

     测试覆盖率工具的典型特性
      1、和Ant集成
      2、多种报告输出格式
      3、源代码链接
      4、覆盖率历史报告

     EMMA的特点

     Instrumentatiton方式:EMMA使用两种模式来实现覆盖率的统计,它称作“offline”和“on-the-fly”。EMMA使用Instrumentation .class文件的方式。EMMA通过byte instrumentation生成.class文件的增强版本,加入了统计测试覆盖率的代码Hook。对于“offline”模式,它从硬盘读入.class文件,然后输出经过Instrumented的增强版本;对于“on-the-fly”模式,这些操作发生在JVM内部(即增强版本的.class文件不写入硬盘)。前者是通用的模式,而后者用于简单的Java应用程序。

      支持的覆盖率指标:EMMA支持class,method,line和basic block coverage指标。
      优越的性能和可伸缩性。
      Java平台支持:EMMA支持Java 1.2或更高版本的JVM,不依赖于任何第三方类库。
      CPL License。
      使用EMMA:命令行方式

     安装EMMA的jar文件到类路径-最简单的方法是,把emma.jar复制到/lib/ext/。注意,不要复制emma_ant.jar,否则使用Ant脚本会出错。
    on-the-fly模式:使用emmarun-使用-g选项编程java源代码,javac -g -d out ,然后执行java emmarun -cp out 。本方法把instrumentation和执行过程合而为一。
    offline模式:分开instrumentation过程和执行过程-首先使用-g选项编译java源代码,javac -g -d out ;然后是instrumentation,java emma instr -d outinstr -ip out,注意,经过instrumentation的class目标目录是outinstr;最后是执行过程,java -cp outinstr;out ,注意,把经过instrumentation的类路径放在前面,并在后面加上原来的类路径,因为instr命令没有处理properties文件和interface,这些都是执行过程需要的。
    使用EMMA:Ant

     设置instrumentation属性

    <property name="coverage.dir" value="${basedir}/coverage" />
    <property name="out.instr.dir" value="${basedir}/outinstr" />
    <property name="emma.enabled" value="true" />
    <property name="javac.debug" value="on" />
    <!-- path element used by EMMA taskdef below: -->
    <path id="emma.lib" >
    <pathelement location="${libs}/emma.jar" />
    <pathelement location="${libs}/emma_ant.jar" />
    </path>

    在Ant脚本中加入EMMA task
    <!-- this loads <emma> and <emmajava> custom tasks: -->
    <taskdef resource="emma_ant.properties" classpathref="emma.lib" />

    在编译Task中打开debug选项
    <target name="compile">
    <mkdir dir="${classes.main}" />
    <javac srcdir="${src.main}" destdir="${classes.main}" debug="${javac.debug}">
    <classpath refid="classpath.lib" />
    </javac>
    <copy todir="${classes.main}">
    <fileset dir="${src.main}" includes="**/*.xml, **/*.vm" />
    </copy>
    </target>

    instrumentation task
    <target name="instrument" depends="compile">
    <mkdir dir="${out.instr.dir}" />
    <mkdir dir="${coverage.dir}" />
    <emma enabled="${emma.enabled}" >
    <instr instrpathref="classpath.main"
    destdir="${out.instr.dir}"
    metadatafile="${coverage.dir}/metadata.emma"
    merge="true"
    >
    <filter excludes="com.talent.fw.formula.test.*Test*,com.talent.fw.esb.*Test*,com.talent.fw.message.test.*,com.
    talent.fw.entityengine.*Test*,com.talent.fw.integration.*Test*,com.talent.fw.security.impl.*Test*,testdomain.*" />
    </instr>
    </emma>
    </target>

    JUnit测试
    <target name="test" depends="compile, instrument">
    <mkdir dir="${reports.junit.data}" />
    <mkdir dir="${classes.main}/maps" />
    <mkdir dir="${classes.main}/scrīpts" />

    <copy todir="${classes.main}/maps">
    <fileset dir="${src.main}/maps" />
    </copy>
    <copy todir="${classes.main}/scrīpts">
    <fileset dir="${src.main}/scrīpts" />
    </copy>
    <copy todir="${classes.main}">
    <fileset dir="${src.main}" includes="*.xml, *.properties, **/*.vm, **/*.dtd" />
    </copy>

    <rmic classname="com.talent.fw.message.test.RMITestServer" base="${classes.main}"/>

    <junit printsummary="yes" haltonfailure="no" failureproperty="tests.failed">
    <classpath location="${out.instr.dir}" />
    <classpath location="${classes.main}" />
    <classpath location="${src.main}" />
    <classpath refid="classpath.lib" />
    <jvmarg value="-Demma.coverage.out.file=${coverage.dir}/coverage.emma" />
    <jvmarg value="-Demma.coverage.out.merge=true" />
    <formatter type="xml" />
    <batchtest fork="yes" todir="${reports.junit.data}" failureproperty="tests.failed">
    <fileset dir="${classes.main}">
    <include name="**/*Test.class" />
    <exclude name="**/AllTests.class" />
    <exclude name="**/Base*Test.class" />
    </fileset>
    </batchtest>
    </junit>
    </target>

    生成报告并复制到Tomcat的发布目录下
    <target name="coverage.report" depends="instrument">
    <!-- if enabled, generate coverage report(s): -->
    <emma enabled="${emma.enabled}" >
    <report sourcepath="${src.main}"
    sort="+block,+name,+method,+class"
    metrics="method:70,block:80,line:80,class:100"
    >
    <fileset dir="${coverage.dir}" >
    <include name="*.emma" />
    </fileset>

    <!-- <xml ōutfile="${coverage.dir}/coverage.xml" depth="package" /> -->
    <html ōutfile="${coverage.dir}/coverage.html" depth="method"
    columns="name,class,method,block,line"
    />
    </report>
    </emma>

    <mkdir dir="${coverage.publish.dir}" />
    <copy todir="${coverage.publish.dir}">
    <fileset dir="${coverage.dir}">
    <include name="**/*.html" />
    </fileset>
    </copy>
    </target>

      和CruiseControl集成
      最简单的集成方式是在cruisecontrol的navigation.jsp文件下方加入EMMA测试覆盖率报告的超链接。如下图:



      进一步的集成方式是,在生成HTML报告的同时,生成XML格式的报告,并为XML格式报告编写XSL文件,并加入到CruiseControl的buildresults.jsp文件中。

     

  • 【转】EMMA:测试覆盖率工具(1)

    2008-6-26

    以前在做过的一个项目上面应用过EMMA,不过当时不是很清楚原理,今天又重新学习了一下。找了两篇不错的帖子跟大家共享一下,呵呵。

    在讨论EMMA的使用之前,我首先简要介绍几个相关的概念。

     测试覆盖率(Code Coverage)
      测试覆盖率,简单的说,就是评价测试活动覆盖产品代码的指标。测试的目的,是确认产品代码按照预期一样工作,也可以看作是产品代码工作方式的说明文档。进一步考虑,测试覆盖率可以看作是产品代码质量的间接指标--之所以说是间接指标,因为测试覆盖率评价的是测试代码的质量,并不是产品代码的质量。

     代码覆盖率是一种白盒测试,因为测试覆盖率是评价产品代码类内部的指标,而不是评价系统接口或规约。测试覆盖率尤其用于评价测试代码是否已经覆盖了产品代码所有的路径。

     衡量测试覆盖率的指标很多,常用的指标有:

     Statement coverage,也称作Line coverage,用于评价测试的代码语句覆盖率。

     Basic block coverage,是Statement coverage的一个变种,它把没有一个分支的代码区域作为一个计量单位,而不是简单的代码行,用于一个if-else分支代码行数远远大于另一个的情况,在这种情况下,statement coverage指标并不适用。

     Decision coverage(也称作Branch coverage),用于评价代码分支地测试覆盖率。

     Path coverage,和Decision coverage相似,用于评价代码从开始到结束所有路径的测试覆盖率。

     Function coverage,用于评价代码方法的测试覆盖率。

     EMMA目前支持四种Coverage类型:class、method、line和basic block。

     测试覆盖率的实现方式

      实现测试服务覆盖率的技术通常分为两种:

     1、Instrumentation

     Instrumentation技术在产品代码的关键位置插入统计代码。事实上,Instrumentation技术可以分为两种方式:Class Instrumentation和Source Instrumentation。前者把统计代码插入编译好的.class文件,而后者则把统计代码插入源代码并编译成新的.class文件。大多数测试覆盖率工具采用这两种Instrumentation技术。

       2、Custom JVM

     另一种方式是在JVM中把统计代码插入.class。测试覆盖率分析可以在JVM执行测试代码的过程中完成。

     测试覆盖率工具的典型特性
      1、和Ant集成
      2、多种报告输出格式
      3、源代码链接
      4、覆盖率历史报告

     EMMA的特点

     Instrumentatiton方式:EMMA使用两种模式来实现覆盖率的统计,它称作“offline”和“on-the-fly”。EMMA使用Instrumentation .class文件的方式。EMMA通过byte instrumentation生成.class文件的增强版本,加入了统计测试覆盖率的代码Hook。对于“offline”模式,它从硬盘读入.class文件,然后输出经过Instrumented的增强版本;对于“on-the-fly”模式,这些操作发生在JVM内部(即增强版本的.class文件不写入硬盘)。前者是通用的模式,而后者用于简单的Java应用程序。

      支持的覆盖率指标:EMMA支持class,method,line和basic block coverage指标。
      优越的性能和可伸缩性。
      Java平台支持:EMMA支持Java 1.2或更高版本的JVM,不依赖于任何第三方类库。
      CPL License。
      使用EMMA:命令行方式

     安装EMMA的jar文件到类路径-最简单的方法是,把emma.jar复制到/lib/ext/。注意,不要复制emma_ant.jar,否则使用Ant脚本会出错。
    on-the-fly模式:使用emmarun-使用-g选项编程java源代码,javac -g -d out ,然后执行java emmarun -cp out 。本方法把instrumentation和执行过程合而为一。
    offline模式:分开instrumentation过程和执行过程-首先使用-g选项编译java源代码,javac -g -d out ;然后是instrumentation,java emma instr -d outinstr -ip out,注意,经过instrumentation的class目标目录是outinstr;最后是执行过程,java -cp outinstr;out ,注意,把经过instrumentation的类路径放在前面,并在后面加上原来的类路径,因为instr命令没有处理properties文件和interface,这些都是执行过程需要的。
    使用EMMA:Ant

     设置instrumentation属性

    <property name="coverage.dir" value="${basedir}/coverage" />
    <property name="out.instr.dir" value="${basedir}/outinstr" />
    <property name="emma.enabled" value="true" />
    <property name="javac.debug" value="on" />
    <!-- path element used by EMMA taskdef below: -->
    <path id="emma.lib" >
    <pathelement location="${libs}/emma.jar" />
    <pathelement location="${libs}/emma_ant.jar" />
    </path>

    在Ant脚本中加入EMMA task
    <!-- this loads <emma> and <emmajava> custom tasks: -->
    <taskdef resource="emma_ant.properties" classpathref="emma.lib" />

    在编译Task中打开debug选项
    <target name="compile">
    <mkdir dir="${classes.main}" />
    <javac srcdir="${src.main}" destdir="${classes.main}" debug="${javac.debug}">
    <classpath refid="classpath.lib" />
    </javac>
    <copy todir="${classes.main}">
    <fileset dir="${src.main}" includes="**/*.xml, **/*.vm" />
    </copy>
    </target>

    instrumentation task
    <target name="instrument" depends="compile">
    <mkdir dir="${out.instr.dir}" />
    <mkdir dir="${coverage.dir}" />
    <emma enabled="${emma.enabled}" >
    <instr instrpathref="classpath.main"
    destdir="${out.instr.dir}"
    metadatafile="${coverage.dir}/metadata.emma"
    merge="true"
    >
    <filter excludes="com.talent.fw.formula.test.*Test*,com.talent.fw.esb.*Test*,com.talent.fw.message.test.*,com.
    talent.fw.entityengine.*Test*,com.talent.fw.integration.*Test*,com.talent.fw.security.impl.*Test*,testdomain.*" />
    </instr>
    </emma>
    </target>

    JUnit测试
    <target name="test" depends="compile, instrument">
    <mkdir dir="${reports.junit.data}" />
    <mkdir dir="${classes.main}/maps" />
    <mkdir dir="${classes.main}/scrīpts" />

    <copy todir="${classes.main}/maps">
    <fileset dir="${src.main}/maps" />
    </copy>
    <copy todir="${classes.main}/scrīpts">
    <fileset dir="${src.main}/scrīpts" />
    </copy>
    <copy todir="${classes.main}">
    <fileset dir="${src.main}" includes="*.xml, *.properties, **/*.vm, **/*.dtd" />
    </copy>

    <rmic classname="com.talent.fw.message.test.RMITestServer" base="${classes.main}"/>

    <junit printsummary="yes" haltonfailure="no" failureproperty="tests.failed">
    <classpath location="${out.instr.dir}" />
    <classpath location="${classes.main}" />
    <classpath location="${src.main}" />
    <classpath refid="classpath.lib" />
    <jvmarg value="-Demma.coverage.out.file=${coverage.dir}/coverage.emma" />
    <jvmarg value="-Demma.coverage.out.merge=true" />
    <formatter type="xml" />
    <batchtest fork="yes" todir="${reports.junit.data}" failureproperty="tests.failed">
    <fileset dir="${classes.main}">
    <include name="**/*Test.class" />
    <exclude name="**/AllTests.class" />
    <exclude name="**/Base*Test.class" />
    </fileset>
    </batchtest>
    </junit>
    </target>

    生成报告并复制到Tomcat的发布目录下
    <target name="coverage.report" depends="instrument">
    <!-- if enabled, generate coverage report(s): -->
    <emma enabled="${emma.enabled}" >
    <report sourcepath="${src.main}"
    sort="+block,+name,+method,+class"
    metrics="method:70,block:80,line:80,class:100"
    >
    <fileset dir="${coverage.dir}" >
    <include name="*.emma" />
    </fileset>

    <!-- <xml ōutfile="${coverage.dir}/coverage.xml" depth="package" /> -->
    <html ōutfile="${coverage.dir}/coverage.html" depth="method"
    columns="name,class,method,block,line"
    />
    </report>
    </emma>

    <mkdir dir="${coverage.publish.dir}" />
    <copy todir="${coverage.publish.dir}">
    <fileset dir="${coverage.dir}">
    <include name="**/*.html" />
    </fileset>
    </copy>
    </target>

      和CruiseControl集成
      最简单的集成方式是在cruisecontrol的navigation.jsp文件下方加入EMMA测试覆盖率报告的超链接。如下图:



      进一步的集成方式是,在生成HTML报告的同时,生成XML格式的报告,并为XML格式报告编写XSL文件,并加入到CruiseControl的buildresults.jsp文件中。

     

  • [转]在Eclipse中使用JUnit

    2008-6-13

    看到一篇很不错的Junit的帖子,打算先转到自己空间来,有时间的时候自己去试试,呵呵。
     
    这篇文章将向你介绍Junit,一个用来在项目中进行测试和调试的工具。在介绍完TDD(以测试驱动开发)理论后,将进一步讲解怎样在流行的Eclipse中建立你自己的JUnit测试。向你展示如何测试Hello World这样简单的程序。

       

        许多书上都讨论了自动测试,但是只有很少的著作注意到这么一个问题,那就是怎样把这些测试组织起来。随着测试的增加,放置和调用这些测试却变得更加麻烦。这将成为一个重要问题,以至于出现了TDD,极限编程(XP)使TDD得以普及。另外,你可以这样理解TDD:通过测试来开发。

       

        TDD的主要规范:

       

        在编写程序代码之前,与之对应的自动测试必须被写好。甚至程序代码并不存在,那也要看见一个失败的测试结果。

        在测试通过后,副本代码必须被丢弃。

       

        有一个具体步骤(可能指的是《Extreme Programming》)可以被任何一个程序员来参考,而不需要特殊的其他方法。在我们开始写测试之前,这些步骤(章节)应该被首先阅读——怎样组织自动测试。

       

        讲解一下不同种类的测试:

       

        单元测试检测模块(也就是类)的正确性。如果对象需要访问外部的数据资源,例如数据库,就需要模拟一个mock objects,但在实际中真实数据与测试环境是不同的。

        客户测试:这是功能性、系统、和验收测试。用来测试整体的系统特性。在XP中,这些测试由用户编写。

        综合测试:介于用户测试和单元测试之间的桥梁。综合测试帮助测试应用程序的交互性。一般情况下,mock objects不被用于综合测试,它会增加测试时间。同样,综合测试经常依赖特殊的测试环境,例如数据库送来的测试数据。综合测试也需要用到外部类库。例如为J2EE应用程序进行综合测试的类库Cactus。解释这些测试超出了本文的范围,需要更加详细的信息请参考http://jakarta.apache.org/cactus/

        开发人员测试:这是用来让开发人员检验自己代码或新函数的。对于每一个开发人员,只要有可能,就需要有更多的测试来检验代码。组织这些测试和组织程序代码一样重要。

       

        在以下章节,只要提到“测试”,那就指的是开发人员测试。

        

        我们几乎准备好开始建立测试了,先应该为我们的测试选择名字。你也许会说,“这不是问题:把‘Test’这个字放在类名前面,就好了!”不会这么快!让我来说一下这个步骤存在的问题:

       

        在TDD中,被测试的类或者方法还不存在。

        一个测试能够覆盖多个方法,甚至多个类,这是可能的。

       

        以上只是一些普遍问题;还存在更多的问题。

       

        让我来提一个建议,在测试命名时:测试类的名字应该让人一眼就知道这是一个测试类,且能说明它要测试什么,注意是否和其他类重名。按照以上建议做,就很简单了,也不用担心名字太长或难听。

       

        即将在Eclipse中用JUnit工具创建我们第一个测试了。假设你已经下载了一个最新的Eclipse版本。如果还没有,你应该去官方站点http://www.eclipse.org下载。还需要JUnit,也可以从http://www.junit.org/下载。

       

        运行Eclipse。新建一个workplace项目,点击文件->新建->项目,选择Java项目,点击下一步。起一个项目名称,例如ProjectWithJUnit。点击完成。这样就完成新项目的建立了。再来配置一下Eclipse,在构建路径中添加JUnit类库。在工具条上点击项目->属性,选择Java构建路径,选择添加外部JAR,浏览Junit被存储的目录,选择junit.jar,点击打开。你将会看见JUnit出现在库的列表中。点击确定,让Eclipse重建路径。

       

        现在开发我们的“Hello World”例子。按照TDD的规则,应该在代码建立以前先把测试写好。为了能够在某出开始,我们假设未来的类名是HelloWorld,并且有一个方法Say(),这个方法返回String的值(例如“Hello World!”)。

       

        建立测试,在ProjectWithJUnit的标题上面点击右键,选择新建->其他,展开“Java”选项,选择JUnit。在右边的栏目对话框中选择测试案例,然后下一步。参考图1。

     

           

                        1. 在Eclipse中建立JUnit测试

       

        在测试类这一栏中,写上将要被测试的类名HelloWorld。选择一个测试案例的名字,例如TestThatWeGetHelloWorldPrompt(是的,看上去很长,但是很清楚它的行为。)点击完成

       

        TestThatWeGetHelloWorldPrompt的代码如下:

     

        import junit.framework.TestCase;

     

        public class TestThatWeGetHelloWorldPrompt

        extends TestCase {

            public TestThatWeGetHelloWorldPrompt(

                String name) {

                super(name);

            }

            public void testSay() {

                HelloWorld hi = new HelloWorld();

                assertEquals("Hello World!", hi.say());

            }

            public static void main(String[] args) {

                junit.textui.TestRunner.run(

                    TestThatWeGetHelloWorldPrompt.class);

            }

        }

     

        代码并不复杂;只是有点与众不同。然而,让我们考察一下细节。我们继承了JUnit的TestCase类,它在JUnit的javadocs定义为“运行众多测试的夹具。”JUnit也有TestSuite类,它是一组测试案例的集合,但在本文中不做讨论。

       

        建立测试案例的步骤如下:

       

        1、建立一个junit.framework.TestCase的实例。

        2、定义一些以“test”开头的无返回方法(例如testWasTransactionSuccessful(),testShow(),等等)。

       

        TestThatWeGetHelloWorldPrompt.java包含这些:TestCase的子类和一个叫做testSay()的方法。这个方法调用了assertEquals()函数,它用来比较我们预期的值和由say()返回的值。

       

        main()方法用来运行测试和显示输出的。JUnit的TestRunner处理测试,提供基于图像和文本的输出表现形式。我们使用基于文本的版本,因为Eclipse支持它,且也适合我们。当开始运行后,基于文本的版本测试会以文本形式输出,Eclipse会把这些输出自动变成图像界面的输出。

       

        按照TDD规范,首次运行测试,应该故意让它失败。点击运行->运行为->Junit测试(记住TestThatWeGetHelloWorldPrompt.java应该被突出的显示在包资源管理器中)。在左边窗口,应该看见JUnit窗口而不是包资源管理器,它显示一个红条,一次失败的测试,具体的失败原因参看图2。如果没有自动显示这些内容,点击JUnit标签(在底部的左边)。

     

              

                        2. JUnit中失败的测试

       

        很好!的却失败了。现在我们来建立被测试代码:在包资源管理器窗口的ProjectWithJUnit标题上右击,选择新建->。选择类名,我们已经假设了它叫HelloWorld,然后直接点击完成。为HelloWorld.java填入下列代码:

     

            public class HelloWorld {

                public String say() {

                    return("Hello World!");

                }

            }

           

        这段代码很简单,甚至不需要注解,我们再来看看结果。按照上面描述过的方式,在JUnit的窗口中显示了一个绿条,参看图3。绿条证明测试成功。

     

              

                         3. JUnit中成功的测试

                                  

        现在,我们想再让测试失败一次,但原因不同。这有助于展示JUnit测试中不同的报错信息。修改assertEquals()代码,把“Hello World!”变成“Hello Me!”。当再次运行JUnit时,结果变成了红条,在JUnit窗口的底部输出了失败原因,参看图4。

     

               

                        4. JUnit中的ComparisonError

                                 

        最后,我想说一下关于测试是开发过程中的必要部分的话题。测试代码一直是开发中的重要部分。经过近几年的发展,已得到了很大的提高,这要归功于强大的理论研究(比如“expectations-based development”等等),和快速发展的测试工具包,还有测试过程的改进。如果你对这篇文章感兴趣,那请你花一些时间来正式的学习一下测试理论吧,这对你的工作很有用。

       

        关于作者:

        Alexander Prohorenko 一名UNIX系统管理员、网络安全管理员。

        Olexiy Prohorenko    一名Java开发者居住在乌克兰的Dniepropetrovsk。

  • 『傻瓜版2』用QTP连接Oracle DB

    2008-5-30

    方法2: 用代码实现QTP与Oracle DB的连接。

    Dim Cnn
    Dim Rst
    Dim strCnn
    strCnn="Provider=OraOLEDB.Oracle.1;Data Source=数据库名;Password=登录密码;User ID=登录用户名;Persist Security Info=True;"

    (得出方法参考一下链接:

    http://bbs.51testing.com/viewthread.php?tid=107598&highlight=DB%2B%CA%FD%BE%DD%BF%E2

    Set Cnn = CreateObject("ADODB.Connection")
    Cnn.Open strCnn
    Set Rst =CreateObject("ADODB.Recordset")
    Rst.open "select column_name  from table_name where **** ", Cnn
    i=1 
    While not Rst.eof       '//循环取出DB里面的数据并导到Global table里面
         dim1=Rst("para1").value
         dim2=Rst("para2").value   
         DataTable.GlobalSheet.SetCurrentRow(i)
         DataTable("Dim1",dtGlobalSheet)=dim1
         DataTable("Dim2",dtGlobalSheet)=dim2     
    i=i+1
        Rst.movenext                 
    wend

  • 『傻瓜版1』用QTP 连接Oracle DB

    2008-5-30

    今天终于成功用QTP连接到数据库了,用的是比较傻瓜的方法,特写出来跟大家分享^_^

    方法1:

    用insert DB checkpoint 的方法:这个方法的关键是创建data resource。

    详细步骤:

    1.Start->All Programs->Orcal OraClient9->Configuration and Migration Tools->Microsoft ODBC Administor 添加你要连接的数据库的data resource 文件,如图1所示。

    2.打开QTP, Insert->check point ->DataBase checkpoint,会出现Database query wizard窗口(图2)。

    3.根据向导一步一步如下图往下做,最后完成之后即可以连接数据库成功添加一个database checkpoint了(图3,图4)。

  • 转载:如何有效的对测试人员进行业绩考核?(08-04-11)

    2008-5-28

    Q:随着企业对软件测试的不断重视,测试团队的规模也越来越大,测试相关的岗位也在逐渐增多,测试工程师、测试leader、高级性能测试工程师等等。如何对不同岗位的测试人员实施科学、合理、公正的考核,已成为测试管理工作的一个重点和难点。如果你是测试经理,你是如何对你的团队进行业绩考核的?如果你是测试工程师,你现在被考核的标准是什么?而你期望的标准又是什么?

    最佳答案由 “godn_1981”提供:

    鄙人从事软件测试好几年了,也一直为该问题困扰,每次项目作完了,感觉大家都表现还马马虎虎。但是项目却感觉总不是那么完美,既然存在问题,说明测试人员的考核做的不到位,所以最近痛定思痛,也咨询了一些在微软和IBM做测试的朋友,结合自己多年来的实践,得出如下核心考核指标,和大家共享。
    测试人员主要是三个方面。
    第一,整体工作效率。第二,工作结果。第三,过程控制。(针对测试主管或组长)

    1.整体工作效率
    1.1有效工作时间
    主要check指标是每日实际工作时间,按照Ms的标准,一个测试工程师的每天的有效工作时间应该在70%以上。如果只有50%或以下的有效工作时间,那么不能成为好的测试工程师,因为他有能力做得更好。
    1.2是否在制定时间内完成工作任务
    主要check指标是进度偏离度。主要是和测试计划相比,有多少的延期。这个指标计算是:计划时间/实际需用时间。
    当然,本指标未考虑其他因素,如开发人员窝工导致的delay。
    2.工作结果
    2.1测试用例的数量和质量
    a,测试用例的数量
    主要考核指标是用例编写速度,考核办法是测试用例的个数/写用例所用时间。
    b,测试用例的质量
    主要考核指标是用例编写质量,用于考察文档是由有效的指导了测试工作。考核办法是来自用例的bug数目/总bug数目,应该是70%以上才算是质量比较好的用例。
    2.2bug的数量和质量
    a,bug提交的数量
    主要考核指标是提交bug的数量,这个指标根据项目不同而定,不好给出固定经验值。
    b,bug的质量
    主要考核指标是提交bug的质量,即提交的bug,严重程度和,发现路径的复杂程度
    c,发现bug的阶段
    主要考核指标是提交bug的时间段,具体执行是统计在测试的每个阶段,发现bug的比例,特别是严重的bug发现的早晚
    2.3是否及时验证关闭bug
    主要考核指标是验证关闭bug的及时度
    2.4测试自动化程度及收效
    主要考核指标是,测试中自动化运用的含量,也就是测试技术含量,成果如何?
    2.5所负责模块的产品总体质量以及用户反馈
    这个总体质量是产品发布之后一段时间才能得出结论,主要是市场,用户对产品的质量、稳定性、性能的反馈。
    考核的主要指标是两个。
    a,根据市场反馈(由经理定性考核)
    b,根据测试人员提交的bug和用户反馈的bug之间的比例,比例越大,说明测试质量相对越高。当然前提是必须划清楚客户的新需求,或者对spec设计方面的抱怨。
    3.过程改进
    考核点,是纵向对比,相比上一个项目,在质量控制上和测试进度进程上有否进步。包括测试方法,提升质量的手段,测试数据记录,用例更新等等有没有改进。
    该项具体考核方法也是经理来根据测试组在过程中具体表现,来定性打分。
    还包括测试人员在测试过程中的学习能力。这个也是定性。
    4.考核注意事项
    4.1统计bug的注意事项
    5.其它注意事项
    作为考核者要注意以下比例,也许有些没有列入考核内容,但是如下这些点可以指导测试。
    a,测试团队发现的bug和所有bug之间的比例
    b,spec设计造成的bug
    c,重复或者误提交的bug所占的比例
    d,每周发现的bug的趋势图
    e,Bug严重等级的构成比例
    f,Bug从提交到解决的平均需要时间
    g,Bug从解决到关闭的平均需要时间
  • 转载:在网站测试中如何做好安全性测试?(08-05-16)

    2008-5-28

    随着网络发展的趋势,对于网站的安全性的要求也越来越高,很多网站都存在被黑客攻击的漏洞,你在网站测试中有做到安全性测试吗?你觉得安全测试应该从哪些方面来检查?

    最佳答案由“卖烧烤的鱼”提供:

    安全性测试(security testing)是有关验证应用程序的安全服务和识别潜在安全性缺陷的过程。

    注意:安全性测试并不最终证明应用程序是安全的,而是用于验证所设立策略的有效性,这些对策是基于威胁分析阶段所做的假设而选择的。


    以下是我读<<软件评测试教程>>中的Web安全性测试章节内容,并进行修改的笔记,前面看了好多朋友写的,不过不是很全,希望对大家有所帮助,建议大家还是买本<<软件评测试教程>>此书绝对物超所值^_^

    WEB安全性测试
    一个完整的WEB安全性测试可以从部署与基础结构、输入验证、身份验证、授权、配置管理、敏感数据、会话管理、加密。参数操作、异常管理、审核和日志记录等几个方面入手。
    1.        安全体系测试
    1)        部署与基础结构
    l        网络是否提供了安全的通信
    l        部署拓扑结构是否包括内部的防火墙
    l        部署拓扑结构中是否包括远程应用程序服务器
    l        基础结构安全性需求的限制是什么
    l        目标环境支持怎样的信任级别
    2)        输入验证
    l        如何验证输入
    A.        是否清楚入口点
    B.        是否清楚信任边界
    C.        是否验证Web页输入
    D.        是否对传递到组件或Web服务的参数进行验证
    E.        是否验证从数据库中检索的数据
    F.        是否将方法集中起来
    G.        是否依赖客户端的验证
    H.       应用程序是否易受SQL注入攻击
    I.        应用程序是否易受XSS攻击
    l        如何处理输入
    3)        身份验证
    l        是否区分公共访问和受限访问
    l        是否明确服务帐户要求
    l        如何验证调用者身份
    l        如何验证数据库的身份
    l        是否强制试用帐户管理措施
    4)        授权
    l        如何向最终用户授权
    l        如何在数据库中授权应用程序
    l        如何将访问限定于系统级资源
    5)        配置管理
    l        是否支持远程管理
    l        是否保证配置存储的安全
    l        是否隔离管理员特权
    6)        敏感数据
    l        是否存储机密信息
    l        如何存储敏感数据
    l        是否在网络中传递敏感数据
    l        是否记录敏感数据
    7)        会话管理
    l        如何交换会话标识符
    l        是否限制会话生存期
    l        如何确保会话存储状态的安全
    8)        加密
    l        为何使用特定的算法
    l        如何确保加密密钥的安全性
    9)        参数操作
    l        是否验证所有的输入参数
    l        是否在参数过程中传递敏感数据
    l        是否为了安全问题而使用HTTP头数据
    10)        异常管理
    l        是否使用结构化的异常处理
    l        是否向客户端公开了太多的信息
    11)        审核和日志记录
    l        是否明确了要审核的活动
    l        是否考虑如何流动原始调用这身份
    2.        应用及传输安全
    WEB应用系统的安全性从使用角度可以分为应用级的安全与传输级的安全,安全性测试也可以从这两方面入手。
    应用级的安全测试的主要目的是查找Web系统自身程序设计中存在的安全隐患,主要测试区域如下。
    l        注册与登陆:现在的Web应用系统基本采用先注册,后登录的方式。
    A.        必须测试有效和无效的用户名和密码
    B.        要注意是否存在大小写敏感,
    C.        可以尝试多少次的限制
    D.        是否可以不登录而直接浏览某个页面等。
    l        在线超时:Web应用系统是否有超时的限制,也就是说,用户登陆一定时间内(例如15分钟)没有点击任何页面,是否需要重新登陆才能正常使用。
    l        操作留痕:为了保证Web应用系统的安全性,日志文件是至关重要的。需要测试相关信息是否写进入了日志文件,是否可追踪。
    l        备份与恢复:为了防范系统的意外崩溃造成的数据丢失,备份与恢复手段是一个Web系统的必备功能。备份与恢复根据Web系统对安全性的要求可以采用多种手段,如数据库增量备份、数据库完全备份、系统完全备份等。出于更高的安全性要求,某些实时系统经常会采用双机热备或多级热备。除了对于这些备份与恢复方式进行验证测试以外,还要评估这种备份与恢复方式是否满足Web系统的安全性需求。
    传输级的安全测试是考虑到Web系统的传输的特殊性,重点测试数据经客户端传送到服务器端可能存在的安全漏洞,以及服务器防范非法访问的能力。一般测试项目包括以下几个方面。
    l        HTTPS和SSL测试:默认的情况下,安全HTTP(Soure HTTP)通过安全套接字SSL(Source Socket Layer)协议在端口443上使用普通的HTTP。HTTPS使用的公共密钥的加密长度决定的HTTPS的安全级别,但从某种意义上来说,安全性的保证是以损失性能为代价的。除了还要测试加密是否正确,检查信息的完整性和确认HTTPS的安全级别外,还要注意在此安全级别下,其性能是否达到要求。
    l        服务器端的脚本漏洞检查:存在于服务器端的脚本常常构成安全漏洞,这些漏洞又往往被黑客利用。所以,还要测试没有经过授权,就不能在服务器端放置和编辑脚本的问题。
    l        防火墙测试:防火墙是一种主要用于防护非法访问的路由器,在Web系统中是很常用的一种安全系统。防火墙测试是一个很大很专业的课题。这里所涉及的只是对防火墙功能、设置进行测试,以判断本Web系统的安全需求。

    另推荐安全性测试工具:
    Watchfire AppScan:商业网页漏洞扫描器(此工具好像被IBM收购了,所以推荐在第一位)
    AppScan按照应用程序开发生命周期进行安全测试,早在开发阶段就进行单元测试和安全保证。Appscan能够扫描多种常见漏洞,例如跨网站脚本、HTTP应答切开、参数篡改、隐藏值篡改、后门/调试选项和缓冲区溢出等等。


    Acunetix Web Vulnerability Scanner:商业漏洞扫描器(目前用的比较多,不过这东东N占内存)
    Acunetix WVS自动检查您的网页程序漏洞,例如SQL注入、跨网站脚本和验证页面弱密码破解。Acunetix WVS有着非常友好的用户界面,还可以生成个性化的网站安全评估报告。

    另附我以前在51testing上发过“Yeepay网站安全测试漏洞之跨站脚本注入
    http://bbs.51testing.com/thread-113784-1-1.html
    Sql注入和跨站脚本这种漏洞比较常见,另在支付宝网站注册页面也存在跨站脚本情况,希望能早点发现^_^

  • 【原创】系统接口测试有感

    2008-1-24

        最近忙于两个系统间的测试。现在将近测试尾期了,自己觉得在测试过程中收获了很多,也有一些值得改进的地方。所以把感触写出来,希望给自己和别人以后的测试工作有所帮助。

        接口是基于两个不同的系统,进行的信息交互。因为是不同的系统,所以各个系统间的很多的设置和业务逻辑是不一样的。可以肯定的一点是系统间还是有部分相同的操作和信息的,要不然也不用做接口了。

        首先,业务分析人员会给出系统间接口的业务逻辑和需求,以及两个系统间的一些字段的mapping表。(就是A系统的某个字段对应到B系统的某个字段,都应该在这个文档中清晰正确的给出)

        QA在拿到需求和匹配文档之后,就应该结合自己所测系统的逻辑跟接口逻辑比较(有些系统的接口逻辑和应用软件的逻辑不一样,接口会增加或放开某些逻辑限制 )。QA应该仔细比较异同处,因为文档当中也有可能有错误。

        在编写测试用例初期,QA应仔细分析需求和进行静态测试。对于mapping表里面的字段,如果是真正系统实施的时候,有些字段是需要做同步的。mapping表里面的系统下拉菜单的字段要特别留意,像下拉菜单里面的value是不是完全匹配的,而且不能只是凭UI上的值去判断,一定要用DB里面的code去判断。因为有些字段,虽然在两个系统UI上的值是一样的,但是在DB里面的code是不一样的,而接口测试肯定是用code来传递参数值的。(我在测试初期就犯了这个错误,只在UI上判断两个系统里面的菜单value。忽略了code,导致在测试过程中很多defect是因为这种原因产生的,而这些其实是可以在测试前发现并提出的。)测试用例的设计基本上采用"End-To-End" 流程模式。

       一般在测试的第一,二个版本上系统都是不稳定的,可能只是实现了需求中的部分功能。所以在测试初期我们不需要去测试很具体的东西,只需要走大概的流程。去确认一下在这个测试版本上实现了哪些功能,哪些功能还没有实现。

       在第三、四个版本上功能基本上都实现了,这个时候我们就需要仔细的测试各个"End-To-End"的flow了,一般还会有挺多的defect的。这两个版本测试过程中,我们应该注意要插入对于mapping表里面所有字段的测试,看是不是所有的字段都能正常显示,某些特殊字段我们还需要去xml文件里面核对一些code。(在这次测试过程中,我的失误就是对于字段的测试进行的太晚,以至于到测试后期出现某个字段的defect导致function flow走不下去的情况。) 这个阶段还有一个值得注意的问题就是,有些字段在第一次从接口传送的时候是可以成功发送的。但是一旦修改后,即使没有修改的一些字段再次发送的时候都有可能被miss掉的。

      在第五、六个版本上,基本上就比较稳定了。但是此时也不能掉以轻心,还是要把所有的flow都仔细的走一遍,可能还会有漏网之鱼呢,呵呵。

      以上就是我这次接口测试的心得,希望对大家的测试有帮助。^_^

      

     

     

     

     

     

     

  • Mysql日期和时间函数

    2007-10-12

    对于每个类型拥有的值范围以及并且指定日期何时间值的有效格式的描述见7.3.6 日期和时间类型。

    这里是一个使用日期函数的例子。下面的查询选择了所有记录,其date_col的值是在最后30天以内:

    mysql> SELECT something FROM table
    WHERE TO_DAYS(NOW()) - TO_DAYS(date_col) <= 30;

    DAYOFWEEK(date)
    返回日期date的星期索引(1=星期天,2=星期一, ……7=星期六)。这些索引值对应于ODBC标准。
    mysql> select DAYOFWEEK('1998-02-03');
    -> 3

    WEEKDAY(date)
    返回date的星期索引(0=星期一,1=星期二, ……6= 星期天)。
    mysql> select WEEKDAY('1997-10-04 22:23:00');
    -> 5
    mysql> select WEEKDAY('1997-11-05');
    -> 2

    DAYOFMONTH(date)
    返回date的月份中日期,在1到31范围内。
    mysql> select DAYOFMONTH('1998-02-03');
    -> 3

    DAYOFYEAR(date)
    返回date在一年中的日数, 在1到366范围内。
    mysql> select DAYOFYEAR('1998-02-03');
    -> 34

    MONTH(date)
    返回date的月份,范围1到12。
    mysql> select MONTH('1998-02-03');
    -> 2

    DAYNAME(date)
    返回date的星期名字。
    mysql> select DAYNAME("1998-02-05");
    -> 'Thursday'

    MONTHNAME(date)
    返回date的月份名字。
    mysql> select MONTHNAME("1998-02-05");
    -> 'February'

    QUARTER(date)
    返回date一年中的季度,范围1到4。
    mysql> select QUARTER('98-04-01');
    -> 2

    WEEK(date)
     
    WEEK(date,first)
    对于星期天是一周的第一天的地方,有一个单个参数,返回date的周数,范围在0到52。2个参数形式WEEK()允许
    你指定星期是否开始于星期天或星期一。如果第二个参数是0,星期从星期天开始,如果第二个参数是1,
    从星期一开始。
    mysql> select WEEK('1998-02-20');
    -> 7
    mysql> select WEEK('1998-02-20',0);
    -> 7
    mysql> select WEEK('1998-02-20',1);
    -> 8

    YEAR(date)
    返回date的年份,范围在1000到9999。
    mysql> select YEAR('98-02-03');
    -> 1998

    HOUR(time)
    返回time的小时,范围是0到23。
    mysql> select HOUR('10:05:03');
    -> 10

    MINUTE(time)
    返回time的分钟,范围是0到59。
    mysql> select MINUTE('98-02-03 10:05:03');
    -> 5

    SECOND(time)
    回来time的秒数,范围是0到59。
    mysql> select SECOND('10:05:03');
    -> 3

    PERIOD_ADD(P,N)
    增加N个月到阶段P(以格式YYMM或YYYYMM)。以格式YYYYMM返回值。注意阶段参数P不是日期值。
    mysql> select PERIOD_ADD(9801,2);
    -> 199803

    PERIOD_DIFF(P1,P2)
    返回在时期P1和P2之间月数,P1和P2应该以格式YYMM或YYYYMM。注意,时期参数P1和P2不是日期值。
    mysql> select PERIOD_DIFF(9802,199703);
    -> 11

    DATE_ADD(date,INTERVAL expr type)
     
    DATE_SUB(date,INTERVAL expr type)
     
    ADDDATE(date,INTERVAL expr type)
     
    SUBDATE(date,INTERVAL expr type)
    这些功能执行日期运算。对于MySQL 3.22,他们是新的。ADDDATE()和SUBDATE()是DATE_ADD()和DATE_SUB()的同义词。
    在MySQL 3.23中,你可以使用+和-而不是DATE_ADD()和DATE_SUB()。(见例子)date是一个指定开始日期的
    DATETIME或DATE值,expr是指定加到开始日期或从开始日期减去的间隔值一个表达式,expr是一个字符串;它可以以
    一个“-”开始表示负间隔。type是一个关键词,指明表达式应该如何被解释。EXTRACT(type FROM date)函数从日期
    中返回“type”间隔。下表显示了type和expr参数怎样被关联: type值 含义 期望的expr格式
    SECOND 秒 SECONDS
    MINUTE 分钟 MINUTES
    HOUR 时间 HOURS
    DAY 天 DAYS
    MONTH 月 MONTHS
    YEAR 年 YEARS
    MINUTE_SECOND 分钟和秒 "MINUTES:SECONDS"
    HOUR_MINUTE 小时和分钟 "HOURS:MINUTES"
    DAY_HOUR 天和小时 "DAYS HOURS"
    YEAR_MONTH 年和月 "YEARS-MONTHS"
    HOUR_SECOND 小时, 分钟, "HOURS:MINUTES:SECONDS"
    DAY_MINUTE 天, 小时, 分钟 "DAYS HOURS:MINUTES"
    DAY_SECOND 天, 小时, 分钟, 秒 "DAYS HOURS:MINUTES:SECONDS"

    MySQL在expr格式中允许任何标点分隔符。表示显示的是建议的分隔符。如果date参数是一个DATE值并且你的计算仅仅
    包含YEAR、MONTH和DAY部分(即,没有时间部分),结果是一个DATE值。否则结果是一个DATETIME值。

    mysql> SELECT "1997-12-31 23:59:59" + INTERVAL 1 SECOND;
    -> 1998-01-01 00:00:00
    mysql> SELECT INTERVAL 1 DAY + "1997-12-31";
    -> 1998-01-01
    mysql> SELECT "1998-01-01" - INTERVAL 1 SECOND;
    -> 1997-12-31 23:59:59
    mysql> SELECT DATE_ADD("1997-12-31 23:59:59",
    INTERVAL 1 SECOND);
    -> 1998-01-01 00:00:00
    mysql> SELECT DATE_ADD("1997-12-31 23:59:59",
    INTERVAL 1 DAY);
    -> 1998-01-01 23:59:59
    mysql> SELECT DATE_ADD("1997-12-31 23:59:59",
    INTERVAL "1:1" MINUTE_SECOND);
    -> 1998-01-01 00:01:00
    mysql> SELECT DATE_SUB("1998-01-01 00:00:00",
    INTERVAL "1 1:1:1" DAY_SECOND);
    -> 1997-12-30 22:58:59
    mysql> SELECT DATE_ADD("1998-01-01 00:00:00",
    INTERVAL "-1 10" DAY_HOUR);
    -> 1997-12-30 14:00:00
    mysql> SELECT DATE_SUB("1998-01-02", INTERVAL 31 DAY);
    -> 1997-12-02
    mysql> SELECT EXTRACT(YEAR FROM "1999-07-02");
    -> 1999
    mysql> SELECT EXTRACT(YEAR_MONTH FROM "1999-07-02 01:02:03");
    -> 199907
    mysql> SELECT EXTRACT(DAY_MINUTE FROM "1999-07-02 01:02:03");
    -> 20102

    如果你指定太短的间隔值(不包括type关键词期望的间隔部分),MySQL假设你省掉了间隔值的最左面部分。例如,
    如果你指定一个type是DAY_SECOND,值expr被希望有天、小时、分钟和秒部分。如果你象"1:10"这样指定值,
    MySQL假设日子和小时部分是丢失的并且值代表分钟和秒。换句话说,"1:10" DAY_SECOND以它等价于"1:10" MINUTE_SECOND
    的方式解释,这对那MySQL解释TIME值表示经过的时间而非作为一天的时间的方式有二义性。如果你使用确实不正确的日期,
    结果是NULL。如果你增加MONTH、YEAR_MONTH或YEAR并且结果日期大于新月份的最大值天数,日子在新月用最大的天调整。

    mysql> select DATE_ADD('1998-01-30', Interval 1 month);
    -> 1998-02-28

    注意,从前面的例子中词INTERVAL和type关键词不是区分大小写的。
    TO_DAYS(date)
    给出一个日期date,返回一个天数(从0年的天数)。
    mysql> select TO_DAYS(950501);
    -> 728779
    mysql> select TO_DAYS('1997-10-07');
    -> 729669

    TO_DAYS()不打算用于使用格列高里历(1582)出现前的值。

    FROM_DAYS(N)
    给出一个天数N,返回一个DATE值。
    mysql> select FROM_DAYS(729669);
    -> '1997-10-07'

    TO_DAYS()不打算用于使用格列高里历(1582)出现前的值。

    DATE_FORMAT(date,format)
    根据format字符串格式化date值。下列修饰符可以被用在format字符串中: %M 月名字(January……December)
    %W 星期名字(Sunday……Saturday)
    %D 有英语前缀的月份的日期(1st, 2nd, 3rd, 等等。)
    %Y 年, 数字, 4 位
    %y 年, 数字, 2 位
    %a 缩写的星期名字(Sun……Sat)
    %d 月份中的天数, 数字(00……31)
    %e 月份中的天数, 数字(0……31)
    %m 月, 数字(01……12)
    %c 月, 数字(1……12)
    %b 缩写的月份名字(Jan……Dec)
    %j 一年中的天数(001……366)
    %H 小时(00……23)
    %k 小时(0……23)
    %h 小时(01……12)
    %I 小时(01……12)
    %l 小时(1……12)
    %i 分钟, 数字(00……59)
    %r 时间,12 小时(hh:mm:ss [AP]M)
    %T 时间,24 小时(hh:mm:ss)
    %S 秒(00……59)
    %s 秒(00……59)
    %p AM或PM
    %w 一个星期中的天数(0=Sunday ……6=Saturday )
    %U 星期(0……52), 这里星期天是星期的第一天
    %u 星期(0……52), 这里星期一是星期的第一天
    %% 一个文字“%”。

    所有的其他字符不做解释被复制到结果中。

    mysql> select DATE_FORMAT('1997-10-04 22:23:00', '%W %M %Y');
    -> 'Saturday October 1997'
    mysql> select DATE_FORMAT('1997-10-04 22:23:00', '%H:%i:%s');
    -> '22:23:00'
    mysql> select DATE_FORMAT('1997-10-04 22:23:00',
    '%D %y %a %d %m %b %j');
    -> '4th 97 Sat 04 10 Oct 277'
    mysql> select DATE_FORMAT('1997-10-04 22:23:00',
    '%H %k %I %r %T %S %w');
    -> '22 22 10 10:23:00 PM 22:23:00 00 6'
    MySQL3.23中,在格式修饰符字符前需要%。在MySQL更早的版本中,%是可选的。

    TIME_FORMAT(time,format)
    这象上面的DATE_FORMAT()函数一样使用,但是format字符串只能包含处理小时、分钟和秒的那些格式修饰符。
    其他修饰符产生一个NULL值或0。
    CURDATE()
     
    CURRENT_DATE
    以'YYYY-MM-DD'或YYYYMMDD格式返回今天日期值,取决于函数是在一个字符串还是数字上下文被使用。
    mysql> select CURDATE();
    -> '1997-12-15'
    mysql> select CURDATE() + 0;
    -> 19971215

    CURTIME()
     
    CURRENT_TIME
    以'HH:MM:SS'或HHMMSS格式返回当前时间值,取决于函数是在一个字符串还是在数字的上下文被使用。
    mysql> select CURTIME();
    -> '23:50:26'
    mysql> select CURTIME() + 0;
    -> 235026

    NOW()
     
    SYSDATE()
     
    CURRENT_TIMESTAMP
    以'YYYY-MM-DD HH:MM:SS'或YYYYMMDDHHMMSS格式返回当前的日期和时间,取决于函数是在一个字符串还是在数字的
    上下文被使用。
    mysql> select NOW();
    -> '1997-12-15 23:50:26'
    mysql> select NOW() + 0;
    -> 19971215235026

    UNIX_TIMESTAMP()
     
    UNIX_TIMESTAMP(date)
    如果没有参数调用,返回一个Unix时间戳记(从'1970-01-01 00:00:00'GMT开始的秒数)。如果UNIX_TIMESTAMP()用一
    个date参数被调用,它返回从'1970-01-01 00:00:00' GMT开始的秒数值。date可以是一个DATE字符串、一个DATETIME
    字符串、一个TIMESTAMP或以YYMMDD或YYYYMMDD格式的本地时间的一个数字。
    mysql> select UNIX_TIMESTAMP();
    -> 882226357
    mysql> select UNIX_TIMESTAMP('1997-10-04 22:23:00');
    -> 875996580

    当UNIX_TIMESTAMP被用于一个TIMESTAMP列,函数将直接接受值,没有隐含的“string-to-unix-timestamp”变换。

    FROM_UNIXTIME(unix_timestamp)
    以'YYYY-MM-DD HH:MM:SS'或YYYYMMDDHHMMSS格式返回unix_timestamp参数所表示的值,取决于函数是在一个字符串
    还是或数字上下文中被使用。
    mysql> select FROM_UNIXTIME(875996580);
    -> '1997-10-04 22:23:00'
    mysql> select FROM_UNIXTIME(875996580) + 0;
    -> 19971004222300

    FROM_UNIXTIME(unix_timestamp,format)
    返回表示 Unix 时间标记的一个字符串,根据format字符串格式化。format可以包含与DATE_FORMAT()函数列出的条
    目同样的修饰符。
    mysql> select FROM_UNIXTIME(UNIX_TIMESTAMP(),
    '%Y %D %M %h:%i:%s %x');
    -> '1997 23rd December 03:43:30 x'

    SEC_TO_TIME(seconds)
    返回seconds参数,变换成小时、分钟和秒,值以'HH:MM:SS'或HHMMSS格式化,取决于函数是在一个字符串还是在数字
    上下文中被使用。
    mysql> select SEC_TO_TIME(2378);
    -> '00:39:38'
    mysql> select SEC_TO_TIME(2378) + 0;
    -> 3938

    TIME_TO_SEC(time)
    返回time参数,转换成秒。
    mysql> select TIME_TO_SEC('22:23:00');
    -> 80580
    mysql> select TIME_TO_SEC('00:39:38');
    -> 2378

Open Toolbar