搭建一個UT測試用例過程中關聯和繼承的選擇
首先交代下背景:
在做軟件的UT時候,框架使用繼承的方式進行搭建,如下圖所示:
類CTestCase是一個父類,包含了所有測試中公用的方法。
其中虛函數RunTest()作為對外啟動測試的虛接口。
Protect類型的CommonWorks()函數包含了一些必須的公用操作,包含了不可重入的一些變量和操作,將被具體測試用例調用。
子類CConcreteTestCaseA是對軟件產品具體某一個特性的測試:
其中屬性mTestAPara是測試A所獨有的針對A特性的一些配置參數;
SpecialWorksForCaseA是A特性特殊的一些操作封裝;
繼承的方法RunTest用來實現具體的對特性A的操作,包括對特殊操作封裝函數SpecialWorksForCaseA的調用;
現在的問題是,新出現了一個特性B,測試它需要調用CConcreteTestCaseA::SpecialWorksForCaseA,
但是目前已知只有極少部分特性的測試需要調用這個操作,其他特性并不需要。
我們可以考慮將B繼承自A,構成3層繼承關系,如下所示:
這樣做的好處在于所有不可重入的變量和方法都會被保護起來。
但是從邏輯上出現了 “B is a A” 的悖論。
另一種選擇則是使用關聯關系,A與B保持邏輯上的sibling的關系不變,但是使用關聯來實現一種類似于單一Composite的結構,如下所示:
這樣做的好處在于如下幾點:
主要的優點是保持了A與B邏輯上的關系正確性,而非“認兄為父”;
當A已經存在的時候,不需要更多額外的修改就可以完成這種工作;
相比于直接復制SpecialWorksForCaseA中的操作,當A邏輯發生改變的時候,更加容易更新;
相比于將SpecialWorksForCaseA提取到父類的CommonWorks的做法,縮減了父類的復雜性和規模,邏輯上成立。
缺點在于:
當A中的mTestArgs依賴于父類的mTestConfiguration時候,類CaseA的實例化需要增加復雜度。