junit设计模式分析二(组合模式)

为了获得对系统测试的信心,需要运行多个测试用例。通过使用Command模式,JUnit能够方便的运行一个单独的测试用例之后产生测试结果。可是在实际的测试过程中,需要把多个测试用例进行组合成为一个复合的测试用例,当作一个请求发送给JUnit.这样JUnit就会面临一个问题,必须考虑测试请求的类型,是一个单一的TestCase还是一个复合的TestCase,甚至要区分到底有多少个TestCase。这样Junit框架就要完成像下面这样的代码:
if(isSingleTestCase(objectRequest)){
//如果是单个的TestCase,执行run,获得测试结果
(TestCase)objectRequest.run()
}else if(isCompositeTestCase(objectRequest)){
//如果是一个复合TestCase,就要执行不同的操作,然后进行复杂的算法进行分
//解,之后再运行每一个TestCase,最后获得测试结果,同时又要考虑
//如果中间测试出现错误怎么办????、

…………………………
…………………………
}
这会使JUnit必须考虑区分请求(TestCase)的类型(是单个testCase还是复合testCase),而实际上大多数情况下,测试人员认为这两者是一样的。对于这两者的区别使用,又会使测试用例的编写变得更加复杂,难以维护和扩展。于是要考虑,怎样设计JUnit才可以实现不需要区分单个TestCase还是复合TestCase,把它们统一成相同的请求?

 

 

当JUnit不必区分其运行的是一个或多个测试用例时,能够轻松地解决这个问题的模式就是Composite(组合)模式。摘引其意图,"将对象组合成树形结构以表示'部分-整体'的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。"在这里'部分-整体'的层次结构是解决问题的关键,可以把单个的TestCase看作部分,而把复合的TestCase看作整体(称为TestSuit)。这样使用该模式便可以恰到好处得解决了这个难题。

 

组合模式的构成:

1、Component:这是一个抽象角色,它给参加组合的对象规定一个接口。这个角色给出共有的接口和默认的行为。其实就我们的Test接口,它定义出run方法

2、Composite:实现共有接口并维护一个测试用例的集合,它就是复合测试用例TestSuite

3、Leaf:代表参加组合的对象,它没有下级子对象,仅定义出参加组合的原始对象的行为,其实就是单一的测试用例TestCase,它仅实现Test接口的方法

 

其实componsite模式根据所实现的接口类型区分为两种形式,分别称为安全式和透明式。JUnit中使用了安全式的结构,这样在TestCase中没有管理子对象的方法。

 

组合模式的代码实现:

Component:

 

public interface Component {
	public void doSomething();
}

 

Composite:

public class Composite implements Component {

	private List list = new ArrayList();

	public void add(Component component) {
		list.add(component);
	}

	public void remove(Component component) {
		list.remove(component);
	}

	public List getAll() {
		return list;
	}

	public void doSomething() {
		for (Component com : list) {
			com.doSomething();
		}
	}
}

 

 

Leaf:

public class Leaf implements Component {
	public void doSomething() {
		System.out.println("dosomething");
	}
}

 

Client:

public class Client {
	public static void main(String[] args) {
		Component com = new Leaf();
		Component com2 = new Leaf();

		Composite composite = new Composite();

		composite.add(com);
		composite.add(com2);

		composite.doSomething();
	}
}

 

 

composite模式告诉我们要引入一个Component抽象类,为Leaf对象和composite对象定义公共的接口。这个类的基本意图就是定义一个接口。在Java中使用Composite模式时,优先考虑使用接口,而非抽象类,因此引入一个Test接口。当然我们的leaf就是TestCase了。其源代码如下:

public interface Test {
       public abstract void run(TestResult result);
}

public abstract class TestCase extends Assert implements Test {
      public void run(TestResult result) {
	result.run(this);}
}

 

下面,列出Composite源码。将其取名为TestSuit类。TestSuit有一个属性fTests (Vector类型)中保存了其子测试用例,提供addTest方法来实现增加子对象TestCase ,并且还提供testCount 和tests 等方法来操作子对象。最后通过run()方法实现对其子对象进行委托(delegate),最后还提供addTestSuite方法实现递归,构造成树形。

 

public class TestSuite implements Test {
	private Vector fTests= new Vector(10);
	public void addTest(Test test) {
		fTests.addElement(test);
	}
	public void addTestSuite(Class testClass) {
		addTest(new TestSuite(testClass));
	}
	public void run(TestResult result) {
		for (Enumeration e= tests(); e.hasMoreElements(); ) {
	  		if (result.shouldStop() )
	  			break;
			Test test= (Test)e.nextElement();
			runTest(test, result);
		}
	}
	public Enumeration tests() {
		return fTests.elements();
	}}

 注意所有上面的代码是对Test接口进行实现的。由于TestCase和TestSuit两者都符合Test接口,我们可以通过addTestSuite递归地将TestSuite再组合成TestSuite,这样将构成树形结构。所有开发者都能够创建他们自己的TestSuit。测试人员可创建一个组合了这些测试用例的TestSuit来运行它们所有的TestCase。

public static Test suite() {
	TestSuite suite1 = new TestSuite("我的测试TestSuit1");
	TestSuite suite2 = new TestSuite("我的测试TestSuit2");
	suite1.addTestSuite(untitled6.Testmath.class);
	suite2.addTestSuite(untitled6.Testmulti.class);
	suite1.addTest(suite2);
	return suite1;
}

 

效果:
我们来考虑经过使用Composite模式后给系统的架构带来了那些效果:
1、 简化了JUnit的代码   JUnit可以统一处理组合结构TestSuite和单个对象TestCase。使JUnit开发变得简单容易,因为不需要区分部分和整体的区别,不需要写一些充斥着if else的选择语句。
2、定义了TestCase对象和TestSuite的类层次结构基本对象TestCase可以被组合成更复杂的组合对象TestSuite,而这些组合对象又可以被组合,如上个例子,这样不断地递归下去。在程序的代码中,任何使用基本对象的地方都可方便的使用组合对象,大大简化系统维护和开发。
3、使得更容易增加新的类型的TestCase,如下面介绍的Decorate模式来扩展TestCase的功能

 

 

其实junit3.8中用到的组合模式是安全式:

添加Component对象的操作定义在Composite角色中,这样的话Leaf就无需实现这些方法(因为Leaf本身根本不需要实现这些方法)

 

还有一种的透明式:

 添加Component对象的操作定义在Component角色中,这样的话不仅Composite需要实现这些方法,Leaf也需要实现这些方法, 而这些方法对于Leaf来说没有任何意义,不过将系统实现统一起来了,因此对用户来说透明(用户无需区分Composite还是Leaf),因为这些角色中都具备这些方法。

 

透明式的代码实现如下:

Component:

public interface Component {
	public void doSomething();
	public void add(Component component);
	public void remove(Component component) ;
	public List getAll() ;
}

 

Composite:

public class Composite implements Component {

	private List list = new ArrayList();

	public void add(Component component) {
		list.add(component);
	}

	public void remove(Component component) {
		list.remove(component);
	}

	public List getAll() {
		return list;
	}

	public void doSomething() {
		for (Component com : list) {
			com.doSomething();
		}
	}
}

 

Leaf:

public class Leaf implements Component {
	public void doSomething() {
		System.out.println("dosomething");
	}

	public void add(Component component) {
	}

	public List getAll() {
		return null;
	}

	public void remove(Component component) {
	}
}

 

Client:

public class Client {
	public static void main(String[] args) {
		Component com = new Leaf();
		Component com2 = new Leaf();

		Component component = new Composite();

		component.add(com);
		component.add(com2);

		component.doSomething();
	}
}

 

 

 

你可能感兴趣的:(junit框架)