必需的第一項(xiàng)更改是使測(cè)試類(lèi)成為 RMock TestCase,而不是成為 jMock CGLIB TestCase。目的是在測(cè)試本身內(nèi)啟用屬于 RMock 的那些模擬對(duì)象的較容易的配置并且 —— 更重要的是 —— 在初設(shè)置期間啟用這些配置。經(jīng)驗(yàn)證明,如果測(cè)試類(lèi)擴(kuò)展的整個(gè) TestCase 對(duì)象屬于 RMock,則通過(guò)兩個(gè)框架構(gòu)造和使用模擬對(duì)象將更容易。此外,乍看之下,快速確定模擬對(duì)象的流程將更容易一些(在這里,流程 用于描述使用模擬對(duì)象作為參數(shù)甚或作為其他模擬對(duì)象的返回類(lèi)型的情況)。
必需的第二項(xiàng)更改是(至少)構(gòu)造一個(gè)保存?zhèn)鬟f給 Collaborator 類(lèi)的構(gòu)造函數(shù)的參數(shù)實(shí)際值的對(duì)象數(shù)組。為了清晰起見(jiàn),還可以包括構(gòu)造函數(shù)接受的類(lèi)類(lèi)型的類(lèi)型數(shù)組并可以傳遞該數(shù)組,以及剛剛描述為參數(shù)的對(duì)象數(shù)組以實(shí)例化模擬 Collaborator 對(duì)象。
第三項(xiàng)更改涉及用正確語(yǔ)法構(gòu)造對(duì) RMock 模擬對(duì)象的一個(gè)或多個(gè)期望。而第四項(xiàng)也是后一項(xiàng)必需的更改是使 RMock 模擬對(duì)象脫離記錄狀態(tài)轉(zhuǎn)入緒狀態(tài)。
實(shí)現(xiàn) RMock 更改
清單 9 顯示了對(duì) ServiceClassTest 類(lèi)的終修改。它還顯示了 RMock 及其相關(guān)功能的引入。
清單 9. 修正場(chǎng)景 3 的 ServiceClassTest 類(lèi)
...
import com.agical.rmock.extension.junit.RMockTestCase;
public class ServiceClassTest extends RMockTestCase {
private ServiceClass serviceClass;
private Collaborator collaborator;
public void setUp(){
serviceClass = new ServiceClass();
Object[] objectArray = new Object[]{"exampleString", 5};
collaborator =
(Collaborator)intercept(Collaborator.class, objectArray, "mockCollaborator");
}
public void testRunServiceAndReturnFalse(){
collaborator.executeJob();
modify().returnValue("failure");
startVerification();
boolean result = serviceClass.runService(collaborator);
assertFalse(result);
}
}
首先,需要注意測(cè)試的期望仍未改變。RMockTestCase 類(lèi)的導(dǎo)入預(yù)示著引入 RMock 框架功能。接下來(lái),測(cè)試類(lèi)現(xiàn)在將擴(kuò)展 RMockTestCase,而不是 MockObjectTestCase。稍后,我將向您展示在 TestClass 對(duì)象仍為 RMockTestCase 類(lèi)型的對(duì)象的測(cè)試用例中重新引入 MockObjectTestCase。
使用 intercept() 方法的備選方法
通過(guò) RMock,您可以使用 intercept() 方法僅模擬具體類(lèi)?梢允褂 RMock mock() 方法模擬具體類(lèi)和接口。僅當(dāng)需要模擬那幾個(gè)重要方法時(shí),使用 interface() 方法。此方法被視為經(jīng)過(guò)改進(jìn)的 mock() 方法。
在 setUp() 方法內(nèi),用 Collaborator 類(lèi)的構(gòu)造方法所需的實(shí)際 值實(shí)例化對(duì)象數(shù)組。該數(shù)組被立刻傳遞給 RMock 的 intercept() 方法來(lái)幫助實(shí)例化模擬對(duì)象。方法的簽名類(lèi)似于 jMock CGLIB mock() 方法的簽名,因?yàn)檫@兩種方法將接納惟一模擬對(duì)象標(biāo)識(shí)符作為參數(shù)。模擬對(duì)象到 Collaborator 類(lèi)型的類(lèi)強(qiáng)制轉(zhuǎn)換十分有必要,因?yàn)?intercept() 方法將返回類(lèi)型 Object。
在測(cè)試方法本身 testRunServiceAndReturnFalse() 之內(nèi),您可以看到更多更改。模擬 Collaborator 對(duì)象的 executeJob() 方法將被調(diào)用。在此階段,模擬對(duì)象處于記錄狀態(tài) —— 即簡(jiǎn)單地定義對(duì)象將一直期望的方法調(diào)用,因此,模擬將相應(yīng)地記錄期望。下一行是對(duì)模擬對(duì)象的通知,用于確保當(dāng)它遇到 executeJob() 方法時(shí),它應(yīng)當(dāng)返回字符串 failure。因此,使用 RMock,您只需通過(guò)調(diào)用方法而不調(diào)用模擬對(duì)象(并傳遞它可能需要的任何參數(shù))來(lái)描述期望,然后修改該期望以相應(yīng)地調(diào)整任何返回類(lèi)型。
后,調(diào)用 RMock 方法 startVerification() 把模擬 Collaborator 對(duì)象轉(zhuǎn)為緒狀態(tài)。模擬對(duì)象現(xiàn)已準(zhǔn)備好在 ServiceClass 類(lèi)中作為實(shí)際對(duì)象使用。該方法非常重要并且必須調(diào)用它才能避免測(cè)試初始化失敗。
測(cè)試更改
再次重新運(yùn)行 ServiceClassTest 以達(dá)到終的肯定結(jié)果:在模擬對(duì)象實(shí)例化期間提供的參數(shù)造成了所有差別。圖 6 顯示 JUnit 測(cè)試已經(jīng)通過(guò)。
圖 6. 使用 RMock 的場(chǎng)景 3 測(cè)試成功
assertFalse(result) 代碼行表示與場(chǎng)景 1 相同的測(cè)試期望,而 RMock 像 jMock 以前那樣維持測(cè)試成功。在許多方面,這都十分重要,但是這里更重要的特點(diǎn)可能是實(shí)踐了修正失敗測(cè)試的靈活 原則而不更改測(cè)試期望。惟一的差別是使用了另一個(gè)框架。
在下一個(gè)場(chǎng)景中,您將在一種特殊情況下使用 jMock 和 RMock。沒(méi)有一個(gè)框架能夠僅憑自身實(shí)現(xiàn)正確結(jié)果,除非在測(cè)試內(nèi)形成某種聯(lián)合。
場(chǎng)景 4:jMock 和 RMock 之間的特定協(xié)作
如前所述,我希望檢驗(yàn)兩個(gè)框架必須協(xié)同工作才能實(shí)現(xiàn)某個(gè)結(jié)果的情況。否則,構(gòu)建良好的測(cè)試每次都將失敗。在某些情況下,使用 jMock 還是 RMock 并不重要,例如,當(dāng)需要模擬的接口或類(lèi)存在于已經(jīng)簽名的 JAR 中時(shí)。此類(lèi)情況十分少見(jiàn),但是當(dāng)測(cè)試針對(duì)安全專(zhuān)有的產(chǎn)品(通常是這樣或那樣的一類(lèi)現(xiàn)有軟件)中的應(yīng)用程序編程接口 (API) 編寫(xiě)代碼時(shí)可能會(huì)出現(xiàn)此情況。
清單 10 顯示了兩個(gè)框架完成測(cè)試用例的示例。