JUnit的框架设计及其使用的设计模式

发表于:2009-5-22 11:35

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:未知    来源:网络转载

  3.4、No stupid subclasses - TestCase again

  我们应用命令模式来表示一个测试。命令执行依赖一个这样的方法:execute(),在TestCase称为run(),通过它使命令得到调用,这使得我们能用这个相同的接口实现不同的命令。

  我们需要一个普遍的接口来运行我们的测试。然而所有的测试用例可能是在一个类中用不同的方法实现的,这样可以避免为每一种测试方法创建一个类,从而导致类的数量急剧增长。某个复杂测试用例类也许实现许多不同的测试方法,每个测试方法定义了一个简单测试用例。每个简单测试用例方法有象这样的名字:testMoneyequals或testMoneyAdd,测试用例并不需要遵守那个简单的命令模式接口,同一个Command类的不同实例可以调用不同的测试方法。因此,下一个问题就是,在测试客户(测试的调用者)的眼里,要让所有的测试用例看起来是一样的。

  回顾一下,这个问题被设计模式解决了,我们想到了Adapter模式。Adapter模式的意图就是,将一个已经存在的接口转变为客户所需要的接口。这符合我们的需要,Adapter有几种不同的方式做到这一点。一个方式就是类适配(class adapter),就是用子类来适配接口,具体说就是,用一个子类来继承已有的类,用已有类中的方法来构造客户所需要的新的方法。例如,要将testMoneyequals适配为runTest,我们继承MoneyTest类,覆写runTest方法,这个方法调用testMoneyEquals方法。

  public class TestMoneyEquals extends MoneyTest {

  public TestMoneyEquals() { super("testMoneyEquals"); }

  protected void runTest () { testMoneyEquals(); }

  }

  使用子类适配的方式要求为每个测试用例实现一个子类,这增加了测试者的负担。JUnit框架的一个目标就是,在增加一个用例时尽量保持简单。另外,为每个测试方法创建一个子类也会导致类膨胀,如果有许多类,这些类中就那么一个方法,这是不值得的,为它们取有意义的名字都很困难。

  Java提供了匿名内隐类机制,解决了命名问题。我们用匿名内隐类来达到Adapter目的,且不用命名:

  TestCase test= new MoneyTest("testMoneyEquals ") {

  protected void runTest() { testMoneyEquals(); }

  };

  这比通常的子类继承方便多了,它仍然在编译时进行类型检查,代价是增加了开发人员的负担。Smalltalk Best Practice Patterns描述了这个问题的另外一个解决方案,不同的实例在相同的pluggable behavior下行为表现不同。其思想就是,使用一个类,这个类可以参数化,即根据不同的参数值执行不同的逻辑,因此避免了子类继承。

  最简单的可插入行为(pluggable behavior)形式是可插入选择子(Pluggable Selector)。在SmallTalk中,Pluggable Selector是一个变量,它指向一个方法,是一个方法指针。这个思想不局限于SmallTalk,也适用于Java。在Java中没有方法选择子的概念,然而,Java的反射(reflection)API能根据方法名这个字符串来调用方法,我们能利用Java的反射特性实现Pluggable Selector。通常我们很少使用Java反射,在这里,我们要涉及一个底层结构框架,它实现了反射。

  JUnit提供给测试客户两种选择:或者使用Pluggable Selector,或者使用匿名内隐类。默认地,我们使用Pluggable Selector方式,即runTest方法。在这种方式中,测试用例的名字必须与测试方法的名字一致。如下所示,我们用反射特性调用方法。首先,我们查看方法对象,一旦有了这个方法对象,我们就可以传给它参数,并调用它。由于我们的测试方法不带参数,因此,我们传进一个空的参数数组:

  protected void runTest() throws Throwable {

  Method runMethod= null;

  try {

  runMethod= getClass().getMethod(fName, new Class[0]);

  } catch (NoSuchMethodException e) {

  assert("Method \""+fName+"\" not found", false);

  } try {

  runMethod.invoke(this, new Class[0]);

  }

  // catch InvocationTargetException and IllegalAccessException

  }

  JDK1.1反射API只让我们查找public方法,因此你必须把测试方法定义为public,否则你会得到NoSuchMethodException例外。

  这是该阶段的设计,Adapter模式和Pluggable Selector模式。

图4:TestCase应用了Adapter模式(匿名内隐类)和Pluggable Selector模式

  由于TestCase中只有一个runTest方法,那么是不是说一个TestCase中只能放一个测试方法呢?为此引入Pluggable Selector模式。在TestCase中放置多个名为testXxx()的方法,在new一个TestCase时,用selector指定哪个testXxx方法与模板方法runTest对接。

  3.5、不用担心是一个测试用例还是许多测试用例-TestSuite

  一个系统通常要运行许多测试。现在,JUnit能运行一个测试,并用TestResult报告结果,下一步就是扩展JUnit,让它能运行许多不同的测试。如果测试的调用者并不在意它是运行一个测试还是许多测试,即它用同样的方式运行一个测试和运行许多测试,那么这个问题就解决了。Composite模式可以解决这个问题,它的意图就是,将许多对象组成树状的具有部分/整体层次的结构,Composite让客户用同样的接口处理单个的对象和整体组合对象。部分/整体的层次结构在此很有意义,一个组合测试可能是有许多小的组合测试构成的,小的组合测试可能是有单个的简单测试构成的。

  Composite模式有以下参与者:

  • Component:是一个公共的统一的接口,用于与测试交互,无论这个测试是简单测试还是组合测试。
  • Composite:用于维护测试集合的接口,这个测试集合就是组合测试。
  • Leaf:表示简单测试用例,遵从Component接口。
43/4<1234>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号