常用链接

统计

最新评论

单元测试(Unit Test)(转)

B.1 单元测试(Unit Test)

一个单元(Unit)是指一个可独立进行的工作,独立进行指的是这个工作不受前一次或接下来的工作的结果影响。简单地说,就是不与程序运行时的上下文(Context)发生关系。

如果是在Java程序中,具体来说一个单元可以是指一个方法(Method)。这个方法不依赖于前一次运行的结果,也不牵涉到后一次的运行结果。

举例来说,下面这个程序的gcd()方法可视为一个单元:

Ü MathTool.java

package onlyfun.caterpillar;

public class MathTool {

    public static int gcd(int num1, int num2) {

        int r = 0;

        while(num2 != 0) {

            r = num1 % num2;

            num1 = num2;

            num2 = r;

        }

        return num1;

    }

}

下面的gcd()方法不可视为一个单元,要完成gcd的计算,必须调用setNum1()setNum2()gcd() 3个方法。

Ü MathFoo.java

package onlyfun.caterpillar;

public class MathFoo {

    private static int num1;

    private static int num2;

    public static void setNum1(int n) {

        num1 = n;

    }

    public static void setNum2(int n) {

        num2 = n;

    }

    public static int gcd() {

        int r = 0;

        while(num2 != 0) {

            r = num1 % num2;

            num1 = num2;

            num2 = r;

        }

        return num1;

    }

}

然而要完全使用一个方法来完成一个单元操作在实现上是有困难的,所以单元也可广义解释为数个方法的集合。这数个方法组合为一个单元操作,目的是完成一个任务。

不过设计时仍优先考虑将一个公开的方法设计为单元,辅助的方法则使用设定为私用,尽量不用数个公开的方法来完成一件工作,以保持接口简洁与单元边界清晰。将工作以一个单元进行设计,这使得单元可以重用,并且也使得单元可以进行测试,进而增加类的可重用性。

单元测试指的是对每一个工作单元进行测试,了解其运行结果是否符合我们的要求。例如当编写完MathTool类之后,也许会这么写一个小小的测试程序:

Ü MathToolTest.java

package test.onlyfun.caterpillar;

import onlyfun.caterpillar.MathTool;

public class MathToolTest {

    public static void main(String[] args) {

        if(MathTool.gcd(10, 5) == 5) {

            System.out.println("GCD Test OK!");

        }

        else {

            System.out.println("GCD Test Fail!");

        }

    }

}

在文字模式下使用文字信息显示测试结果,这个动作是开发人员经常作的事情,然而您必须一行一行看着测试程序的输出结果,以了解测试是否成功。另一方面,测试程序本身也是一个程序,在更复杂的测试中,也许会遇到测试程序本身出错,而导致无法验证结果的情况。

JUnit是一个测试框架,通过它所提供的工具,可以减少编写错误的测试程序的机会。另一方面,可以有更好的方法来检验测试结果,而不是看着一长串输出的文字来检验测试是否成功。JUnit测试框架让测试的进行更有效率且更具可靠性。

B.2 JUnit设置

JUnit最初是由Erich GammaKent Beck编写,为单元测试的支持框架,用来编写与执行重复性的测试。它包括以下特性:

Ü   对预期结果作判断

Ü   提供测试装备的生成与销毁

Ü   易于组织与执行测试

Ü   图形与文字接口的测试器

要设定JUnit,可先到 JUnit官方网站(http://junit.org/)下载JUnitzip文件,下载后解开压缩文件,其中会含有junit.jar文件,将这个文件复制到所要的数据夹中,然后设定Classpath指向junit.jar。例如:

set classpath=%classpath%;YOUR_JUNIT_DIR\junit.jar

如果是Windows 2000/XP,可以在环境变量中设定Classpath变量(可参考附录A中的Classpath设置介绍)

B.3 第一个JUnit测试

要对程序进行测试,首先要设计测试案例(Test Case)。一个测试案例是对程序给予假定条件,然后运行程序并看看在给定的条件下,程序的运行结果是否符合要求。

JUnit下,可以继承TestCase来编写测试案例,并定义测试方法,每一个测试方法是以testXXX()来命名。一个例子如下所示:

 

Ü MathToolUnitTest.java

package test.onlyfun.caterpillar;

 

import onlyfun.caterpillar.MathTool;

import junit.framework.TestCase;

 

public class MathToolUnitTest extends TestCase {

    public void testGcd() {

        assertEquals(5, MathTool.gcd(10, 5));

    }

   

    public static void main(String[] args) {

        junit.textui.TestRunner.run(MathToolUnitTest.class);

    }

}

assertEquals()方法用来断定您的预期值与单元方法实际的返回结果是否相同,如果预期值与返回的结果不同则丢出异常,TestRunner会捕捉异常,并提取其中的相关信息以报告测试结果。这里使用的是文字模式的TestRunner

接下来根据测试案例编写实际的程序,首先试着让测试案例能通过编译:

package onlyfun.caterpillar;
 
public class MathTool {
    public static int gcd(int num1, int num2) { 
        return 0;
    }
}

编译完MathTool.java并用javac来编译它。在编译完成之后,接着运行测试案例,会得到以下的结果:

.F
Time: 0
There was 1 failure:
1) testGcd(test.onlyfun.caterpillar.MathToolUnitTest)junit.framework.AssertionFa
iledError: expected:<5> but was:<0>
...
FAILURES!!!
Tests run: 1,  Failures: 1,  Errors: 0

由于MathTool中并没有编写什么实际的逻辑,所以测试失败。在测试驱动中,测试案例所报告的结果通常是以测试失败作为开始,您的挑战就是要一步步消除这些失败的信息。接下来根据测试案例,完成所设计的程序:

Ü MathTool.java

package onlyfun.caterpillar;

 

public class MathTool {

    public static int gcd(int num1, int num2) {

        int r = 0;

        while(num2 != 0) {

            r = num1 % num2;

            num1 = num2;

            num2 = r;

        }

        return num1;

    }

}

再次运行测试案例,会得到以下的结果,通过最后的OK信息,知道测试已经成功:

.Time: 0
 
OK (1 test)

不一定要在main()中指定TestRunner,而可以直接启动一个TestRunner,并指定测试案例类(继承TestCase的类)。例如启动一个Swing窗口的测试结果画面:

java junit.swingui.TestRunner test.onlyfun.caterpillar.MathToolUnitTest

执行的结果画面如图B-1所示。

Swing窗口的测试结果显示中,如果中间的横棒是显示绿色,表示所有的测试都已经成功,如果中间的横棒显示红色,表示测试失败。JUnit的名言Keep the bar green to keep the code clean,意思是保持绿色横棒以保证测试成功。

也可以指定文字模式的测试结果。例如:

java junit.textui.TestRunner test.onlyfun.caterpillar.MathToolUnitTest

B-1  JUnitSwing窗口测试结果

B.4 自动构建与测试

Ant可以进行自动化构建,而JUnit可以进行自动化测试,Ant可以与JUnit结合,使得自动化的构建与测试变得可行。

如果要让Ant能支持JUnit,建议直接将JUnitjunit.jar放置在Antlib目录,并记得改变Classpath中原先有关junit.jar的设定。例如将Classpath重新指向%ANT_HOME%\lib\junit.jar(假设已经如附录A中设置了ant_home的环境变量)。虽然也有其他的方式可以设定,但这是最快也是最简单的方法。

Ant使用<junit>标签来设定JUnit测试,下面是一个简单的例子:

 

<?xml version="1.0"?>

<project name="autoBuildTest" default="test">

    <target name="setProperties">

        <property name="src.dir" value="src"/>

        <property name="classes.dir" value="classes"/>

    </target>

 

    <target name="prepareDir" depends="setProperties">

        <delete dir="${classes.dir}"/>

        <mkdir dir="${classes.dir}"/>

    </target>

 

    <target name="compile" depends="prepareDir">

        <javac srcdir="${src.dir}" destdir="${classes.dir}"/>

    </target>

 

    <target name="test" depends="compile">

        <junit printsummary="yes">

            <test

               name="test.onlyfun.caterpillar.MathToolUnitTest"/>

            <classpath>

                <pathelement location="${classes.dir}"/>

            </classpath>

        </junit>

    </target>

</project>

printsummary属性会将测试的结果简单地显示出来,<test>name属性是设定所要进行测试的测试案例类。Ant构建与调用JUnit进行测试的信息如下:

C:\workspace\B>ant
Buildfile: build.xml
 
setProperties:
 
prepareDir:
    [mkdir] Created dir: C:\workspace\B\classes
 
compile:
    [javac] Compiling 4 source files to C:\workspace\B\classes
 
test:
    [junit] Running test.onlyfun.caterpillar.MathToolUnitTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0 sec
 
BUILD SUCCESSFUL
Total time: 1 second

B.5 自动生成测试报告

接上一个主题,可以将JUnit的测试过程在Ant构建过程中显示出来,只要加入< formatter>标签设定即可:

<?xml version="1.0"?>
<project name="autoBuildTest" default="test">
    ...
    <target name="test" depends="compile"> 
        <junit printsummary="yes"> 
            <formatter type="plain" usefile="false"/>
            <test 
               name="test.onlyfun.caterpillar.MathToolUnitTest"/> 
            <classpath> 
                <pathelement location="${classes.dir}"/> 
            </classpath> 
        </junit> 
    </target>
</project>

Ant构建与调用JUnit进行测试的信息如下:

C:\workspace\B>ant
Buildfile: build.xml
 
setProperties:
 
prepareDir:
   [delete] Deleting directory C:\workspace\B\classes
    [mkdir] Created dir: C:\workspace\B\classes
 
compile:
    [javac] Compiling 4 source files to C:\workspace\B\classes
 
test:
    [junit] Running test.onlyfun.caterpillar.MathToolUnitTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.016 sec
    [junit] Testsuite: test.onlyfun.caterpillar.MathToolUnitTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.016 sec
 
    [junit] Testcase: testGcd took 0.016 sec
 
BUILD SUCCESSFUL
Total time: 2 seconds

usefile属性设定为true时,会自动将产生的结果保存在文件中,默认是TEST-*.txt。其中*是测试案例类名称。就上例而言,所产生的报告文件内容如下:

Testsuite: test.onlyfun.caterpillar.MathToolUnitTest
Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0 sec
 
Testcase: testGcd took 0 sec

<formatter>标签还可以设定将测试的结果,以XML文件保存下来。一个编写的例子如下,它将测试的结果保存至report目录中, 文件名称为TEST-*.xml*是测试案例类名称:

<?xml version="1.0"?>
    ...
    <target name="test" depends="compile"> 
        <junit printsummary="yes"> 
            <formatter type="xml"/>
            <test 
               name="test.onlyfun.caterpillar.MathToolUnitTest"/> 
            <classpath> 
                <pathelement location="${classes.dir}"/> 
            </classpath> 
        </junit> 
    </target>
</project>

也可以将测试结果所产生的XML文件转换为HTML文件,使用Ant可以直接完成这个工作。<junitreport>标签使用 XSLTXML文件转换为HTML文件。下面的例子将前面的说明作个总结,以完整呈现编写的实例:

Ü build.xml

<?xml version="1.0"?>

<project name="autoBuildTest" default="report">

    <target name="setProperties">

        <property name="src.dir" value="src"/>

        <property name="classes.dir" value="classes"/>

        <property name="report.dir" value="report"/>       

    </target>

 

    <target name="prepareDir" depends="setProperties">

        <delete dir="${report.dir}"/>

        <delete dir="${classes.dir}"/>

        <mkdir dir="${report.dir}"/>

        <mkdir dir="${classes.dir}"/>

    </target>

 

    <target name="compile" depends="prepareDir">

        <javac srcdir="${src.dir}" destdir="${classes.dir}"/>

    </target> 

 

    <target name="test" depends="compile">

        <junit printsummary="yes">

            <formatter type="xml"/>

            <test

               name="test.onlyfun.caterpillar.MathToolUnitTest"/>

            <classpath>

                <pathelement location="${classes.dir}"/>

            </classpath>

        </junit>

    </target>

   

    <target name="report" depends="test">

        <junitreport todir="${report.dir}">

            <fileset dir="${report.dir}">

                <include name="TEST-*.xml"/>

            </fileset>

            <report

                format="frames" todir="${report.dir}/html"/>

        </junitreport>

    </target>   

</project>

<include>设定搜寻TEST-*.xml文件,将之转换为HTML文件,而最后的结果被设定保存至report/html/目录下,在format属性中设定了HTML文件具有边框(Frame),如果不设定这个属性,则HTML报告文件就不具有边框。在运行Ant之后所产生的 HTML测试结果报告文件如图B-2所示。

B-2  Ant结合JUnit所自动产生的测试报告

附录B只是对JUnit的一些简介,如果需要更多有关JUnit的资料,可以参考以下的网址:

http://caterpillar.onlyfun.net/Gossip/JUnit/JUnitGossip.htm

posted on 2008-01-08 10:03 九宝 阅读(511) 评论(0)  编辑  收藏 所属分类: Java


只有注册用户登录后才能发表评论。


网站导航: