商讯信箱
用户名: @
密  码:   注册|忘记密码
登录
个人用户经销商
您的位置:首页 > 技术频道 > 正文

使用JUnit高效完成功能测试

作者:Nada daVeiga 译:袁发明  2008-01-04

功能测试
 
    通过确定执行程序功能的主要代码段,可以将测试建立在一个更有效的环境下。由于这些类提供了从系统外部进行测试的途径,所以也是代码的入口点。
    因此,功能测试的整体目标就是确定一组可以访问系统功能的高层接口类。这些类的独立性越高越好。毕竟,如果能将类从环境中分离出来,测试起来会更加容易。
    确定作为入口点的代码是一个简单的过程。在代码库中,通常有几个控制该库所有功能的入口点。这些外部类作为客户端代码,与库的中介对象将开发人员从复杂的代码分析中解脱出来。这些便是应当首先对其方法进行测试的类。
    比如,Saxon有一小组类作为逻辑入口点提供对库的访问。通过对外部类进行编码操作,比如转换、设置和查询,客户端代码可以访问库的许多功能类,而无需考虑类的接口问题,甚至无需担心这些类是否存在。这些外部类用高层易用的接口提供一个简单的方式对系统功能进行测试,这正是一个优良的库的特征。
    程序代码中的各个功能模块通常是各自独立的。在某些代码中,甚至可以认为这些模块各自对应不同的、可通过大量外部类访问的库。这些类查找高层接口的逻辑位置。插件结构通常都采用这种设计模式:每个插件程序都有一个可以有效执行内部代码全部功能的简单接口。
    在一些非严格描述的系统中,通常有一个所有程序行为的中介点。在MVC架构中,这个中介类一般作为“控制器”,负责配置系统各部分的请求路由。整体系统的功能主要由这个控制器连接的类实现,因此,这些类是测试的主要对象。
    比如在Applet程序设计中,java.applet.Applet的派生类就是所有代码的中心处理单元。根据代码的分解程度,测试焦点可以放在Applet子类或与其连接的类上。
    连接各个模块的代码也是测试的主要对象。将应用程序请求转换为数据库查询的类,以及有相似功能的适配类是其次应该考虑的测试对象。
    各种基于MVC(模式-视图-控制器)架构的组件可以用其它的测试框架(比如Junit的扩展)进行测试。例如,Struts的action类就最好使用JUnit的扩展StrutsTestCase进行测试;服务器端的组件(如Servlets、JSP和EJB)最好用Catus进行测试;而HttpUnit则是对Web应用程序进行黑盒测试的最好框架。本文讨论的所有技术都可应用于这些框架环境下的测试。

从用例到测试用例
 
    每个入口点都必须与相应的用例匹配。某些情况下可以忽视这一步,因为类名的自记录可以实现自动匹配,比如Saxon中的转换类可以实现XSL转换,查询类可以进行XQuery转换。
    其它情况则要复杂得多。通常用例描述的功能只能以横切关注点的方式存在,不能用任何单独的类进行例证。只有几组类交互时或满足一定条件时,才能观察到功能行为。这种情况下,测试的初始化程序会比较长,或者可以用setUp()方法提供需要的测试环境。
    而调用代码的运行程序应该尽可能地设计成一行,以减少与被测试代码的关联,这可以有效避免对边缘效应与不稳定实现细节的依赖。测试的检查阶段是最复杂的,因为这个阶段经常需要添写非测试用代码。测试时可能需要对结果进行严格的分析以确保其符合要求。有时甚至需要将这个过程分为几步来完成,以取得测试可以识别的结果。在XSL转换中,这两种情况都是可能的,结果储存在文件中,然后以XML格式读入内存并进行准确性分析。
    Saxon中有个相对简单的例子。已有XML文件和XPath表达式的情况下,Saxon可以执行表达式并返回匹配列表。Saxon中的XpathExample样本类就是用来执行这种任务的。基于以上分析,可以设计如下的测试流程:

    public void testXPathEvaluation() {
      //initialize
      XPathEvaluator xpe = new XPathEvaluator(
        new SAXSource(new InputSource("/path/to/file.xml")));
      XPathExpression findLine =
        xpe.createExpression("/some/xpath[expression]");
      //work
      List matches = findLine.evaluate();
      //check
      assertTrue(matches.count() > 0);
    }

    两次输入的都是字符串常量,输出的则是所匹配的列表,可以用来验证匹配结果的正确性。这些工作都由一行代码完成,这行代码只是简单地调用了被测试的方法。
    另一种可能的情况是XPathEvaluator没有调用createExpression()方法。因为表达式不存在,这时可能会显示错误信息。
    将输入的源文件名和表达式保留在测试用例中不是个好习惯。某些项目(服务器名、用户名和密码等)不应该出现在测试文件中,它们应该可以根据情况自由设置。并且,测试用例的设计应该方便测试驱动和测试数据的分离、测试驱动对大范围数据的可重用性和测试数据对测试驱动的可重用性。另一方面,不要将一个简单的测试用例实现设计地过于复杂。一般来说,测试用例已经说明了系统的大部分状态,并可对其进行参数描述,所以无需在测试中进行过于详细的参数描述。
    许多代码段可能出现在不止一个测试用例中。有经验的面向对象开发人员会尝试对其进行重构并创建通用类和有效方法。有时候这样做非常有用,比如登录过程应该设计成所有测试用例可用的方法。 但是,不要过度设计测试,这些Java类仅仅是用来验证应用程序的功能行为而已。
    测试用例是脆弱的。比如,如果开发人员更改了testXPathEvaluation测试中输入文件的位置,或者creatExpression方法签名有所变动,测试脚本就会失效。
    对于应用程序的测试用例实现来说,大量的重复性工作与改动是不可避免的。因此,可跟踪性对于所有的测试用例都是至关紧要的。出现问题的时候,如果能为开发人员指出相应的测试用例说明和用例说明将有利于提高修正bug的速度。
    因此,测试用例注释中应标明原始说明文档的引用位置。这可以是一个简单的代码注释,也可以对每条测试都注释相关用例和所测功能,这样当测试出现问题时开发人员就会收到一条相关信息。因此,在代码中加入参考并维护可追踪性是很重要的。

1 2 3
【内容导航】
第1页: 确定、转换测试用例 第2页: 功能测试
第3页: 设计运行时事件表
©版权所有。未经许可,不得转载。
[责任编辑:李倩]
[an error occurred while processing this directive]