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<Component> list = new ArrayList<Component>();

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

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

	public List<Component> 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<Component> getAll() ;
}

 

Composite:

public class Composite implements Component {

	private List<Component> list = new ArrayList<Component>();

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

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

	public List<Component> 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<Component> 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();
	}
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章