单元测试基本概念

来源:互联网 发布:java数据连接池原理 编辑:程序博客网 时间:2024/04/30 09:38

                                             单元测试基本概念

        被测系统:SUT(System Under Test)

        被测系统(System under test,SUT)表示正在被测试的系统,目的是测试系统能否正确操作。这一词语常用于软件测试中。软件系统测试的一个特例是对应用软件的测试,称为被测应用程序(application under test,AUT)。
        SUT也表明软件已经到了成熟期,因为系统测试在测试周期中是集成测试的后一阶段。

        测试替身:Test Double

        在单元测试时,使用Test Double减少对被测对象的依赖,使得测试更加单一。同时,让测试案例执行的时间更短,运行更加稳定,同时能对SUT内部的输入输出进行验证,让测试更加彻底深入。但是,Test Double也不是万能的,Test Double不能被过度使用,因为实际交付的产品是使用实际对象的,过度使用Test Double会让测试变得越来越脱离实际。
        要理解测试替身,需要了解一下Dummy Objects、Test Stub、Test Spy、Fake Object这几个概念,下面我们对这些概念分别进行说明。

        Dummy Objects

        Dummy Objects泛指在测试中必须传入的对象,而传入的这些对象实际上并不会产生任何作用,仅仅是为了能够调用被测对象而必须传入的一个东西。

        Test Stub

        测试桩是用来接受SUT内部的间接输入(indirect inputs),并返回特定的值给SUT。可以理解Test Stub是在SUT内部打的一个桩,可以按照我们的要求返回特定的内容给SUT,Test Stub的交互完全在SUT内部,因此,它不会返回内容给测试案例,也不会对SUT内部的输入进行验证。

        Test Spy

        Test Spy像一个间谍,安插在了SUT内部,专门负责将SUT内部的间接输出(indirect outputs)传到外部。它的特点是将内部的间接输出返回给测试案例,由测试案例进行验证,Test Spy只负责获取内部情报,并把情报发出去,不负责验证情报的正确性。

        Mock Object

        Mock Object和Test Spy有类似的地方,它也是安插在SUT内部,获取到SUT内部的间接输出(indirect outputs),不同的是,Mock Object还负责对情报(intelligence)进行验证,总部(外部的测试案例)信任Mock Object的验证结果。

        Fake Object

        经常,我们会把Fake Object和Test Stub搞混,因为它们都和外部没有交互,对内部的输入输出也不进行验证。不同的是,Fake Object并不关注SUT内部的间接输入(indirect inputs)或间接输出(indirect outputs),它仅仅是用来替代一个实际的对象,并且拥有几乎和实际对象一样的功能,保证SUT能够正常工作。实际对象过分依赖外部环境,Fake Object可以减少这样的依赖。

        测试夹具:Test Fixture

        所谓测试夹具(Fixture),就是测试运行程序(test runner)会在测试方法之前自动初始化、回收资源的工作。JUnit4之前是通过setUp、TearDown方法完成。在JUnit4中,仍然可以在每个测试方法运行之前初始化字段和配置环境,当然也是通过注解完成。在JUnit4中,通过@Befroe替代setUp方法;@After替代tearDown方法。在一个测试类中,甚至可以使用多个@Before来注解多个方法,这些方法都是在每个测试之前运行。说明一点,@Before是在每个测试方法运行前均初始化一次,同理@Ater是在每个测试方法运行完毕后均执行一次。也就是说,经这两个注解的初始化和注销,可以保证各个测试之间的独立性而互不干扰,它的缺点是效率低。另外,不需要在超类中显式调用初始化和清除方法,只要它们不被覆盖,测试运行程序将根据需要自动调用这些方法。超类中的@Before方法在子类的@Before方法之前调用(与构造函数调用顺序一致),@After方法是子类在超类之前运行。
        一个测试用例可以包含若干个打上@Test注解的测试方法,测试用例测试一个或多个类API接口的正确性,当然在调用类API时,需要事先创建这个类的对象及一些关联的对象,这组对象就称为测试夹具(Fixture),相当于测试用例的“工作对象”。
        前面讲过,一个测试用例类可以包含多个打上@Test注解的测试方法,在运行时,每个测试方法都对应一个测试用例类的实例。当然,用户可以在具体的测试方法里声明并实例化业务类的实例,在测试完成后销毁它们。但是,这么一来就要在每个测试方法中都重复这些代码,因为TestCase实例依照以下步骤运行。
        创建测试用例的实例。
        使用注解@Before注解修饰用于初始化夹具的方法。
        使用注解@After注解修饰用于注销夹具的方法。
        保证这两种方法都使用 public void 修饰,而且不能带有任何参数。
        TestCase实例运行过程如图16-1所示。
         
        图16-1  方法级别夹具执行示意图
        之所以每个测试方法都需要按以上流程运行,是为了防止测试方法相互之间的影响,因为在同一个测试用例类中不同测试方法可能会使用到相同的测试夹具,前一个测试方法对测试夹具的更改会影响后一个测试方法的现场。而通过如上的运行步骤后,因为每个测试方法运行前都重建运行环境,所以测试方法相互之间就不会有影响了。
        可是,这种夹具设置方式还是引来了批评,因为它效率低下,特别是在设置 Fixture 非常耗时的情况下(例如设置数据库链接)。而且对于不会发生变化的测试环境或者测试数据来说,是不会影响到测试方法的执行结果的,也就没有必要针对每一个测试方法重新设置一次夹具。因此在 JUnit 4 中引入了类级别的夹具设置方法,编写规范说明如下。
        创建测试用例的实例。
        使用注解BeforeClass 修饰用于初始化夹具的方法。 
        使用注解AfterClass 修饰用于注销夹具的方法。
        保证这两种方法都使用 public static void 修饰,而且不能带有任何参数。

        类级别的夹具仅会在测试类中所有测试方法执行之前执行初始化,并在全部测试方法测试完毕之后执行注销方法,如图16-2所示。

        

        图16-2  类级别夹具执行示意图

        测试用例:Test Case

        有了测试夹具,就可以开始编写测试用例的测试方法了。当然也可以不需要测试夹具而直接编写测试用例方法。
        在JUnit 3中,测试方法都必须以test为前缀,且必须是public void的,JUnit 4之后,就没有这个限制,只要在每个测试方法标注@Test注解,方法签名可以是任意取名。
        可以在一个测试用例中添加多个测试方法,运行器为每个方法生成一个测试用例实例并分别运行。

        测试套件:Test Suite

        如果每次只能运行一个测试用例,那么又陷入了传统测试(使用main()方法进行测试)的窘境:手工去运行一个个测试用例,这是非常烦琐和低效的,测试套件专门为解决这一问题而来。它通过TestSuite对象将多个测试用例组装成一个测试套件,则测试套件批量运行。需要特别指出的是,可以把一个测试套件整个添加到另一个测试套件中,就像小筐装进大筐里变成一个筐一样。
        JUnit4中最显著的特性是没有套件(套件机制用于将测试从逻辑上分组并将这这些测试作为一个单元测试来运行)。为了替代老版本的套件测试,套件被两个新注解代替:@RunWith、@SuteClasses。通过@RunWith指定一个特殊的运行器,即Suite.class套件运行器,并通过@SuiteClasses注解,将需要进行测试的类列表作为参数传入。
        创建步骤说明如下。
        创建一个空类作为测试套件的入口(这个空类必须使用public修饰符,而且存在无参构造函数)。
        将@RunWith、@SuiteClasses注释修饰这个空类。
        把Suite.class作为参数传入@RunWith注释,以提示JUnit将此类指定为运行器。
        将需要测试的类组成数组作为@SuiteClasses的参数。

        断言:Assertions

        断言(assertion)是测试框架里面的若干个方法,用来判断某个语句的结果是否为真或判断是否与预期相符。比如assertTrue这一方法就是用来判定一条语句或一个表达式的结果是否为真,如果条件为假,那么该断言就会执行失败。
        在JUnit 4中一个测试类并不继承自TestCase(在JUnit 3.8中,这个类中定义了assertEquals()方法),所以你必须使用前缀语法(举例来说,Assert.assertEquals())或者静态地导入Assert类。这样我们就可以完全像以前一样使用assertEquals方法。
        由于JDK 5.0自动装箱机制的出现,原先的12个assertEquals方法全部去掉了。例如,原先JUnit 3.8中的assertEquals(long,long)方法在JUnit 4中要使用assertEquals(Object,Object),对于assertEquals(byte,byte)、assertEquals(int,int)等也是这样。
        在JUnit 4中,新集成了一个assert关键字。你可以像使用assertEquals方法一样来使用它,因为它们都抛出相同的异常(java.lang.AssertionError)。JUnit 3.8的assertEquals将抛出一个junit.framework.AssertionFailedError。注意,当使用assert时,你必须指定Java的"-ea"参数,否则断言将被忽略。
 

        ——本段文字节选自《Spring 3.x 企业应用开发实战》一书

图书详细信息:http://blog.csdn.net/broadview2006/article/details/7326175


原创粉丝点击