您的位置:軟件測(cè)試 > 開(kāi)源軟件測(cè)試 > 開(kāi)源單元測(cè)試工具 >
EasyMock使用方法與原理剖析
作者:網(wǎng)絡(luò)轉(zhuǎn)載 發(fā)布時(shí)間:[ 2013/2/21 13:32:40 ] 推薦標(biāo)簽:

我們知道 Mock 對(duì)象有兩種創(chuàng)建方式:一種是通過(guò) EasyMock 類提供的 createMock 方法創(chuàng)建,另一種是通過(guò) EasyMock 類的 createControl 方法得到一個(gè) IMocksControl 實(shí)例,再由這個(gè) IMocksControl 實(shí)例創(chuàng)建 Mock 對(duì)象。其實(shí),無(wú)論通過(guò)哪種方法獲得 Mock 對(duì)象,EasyMock 都會(huì)生成一個(gè) IMocksControl 的實(shí)例,只不過(guò)第一種方式中的 IMocksControl 的實(shí)例對(duì)開(kāi)發(fā)人員不可見(jiàn)而已。這個(gè) IMocksControl 的實(shí)例,其實(shí)是 MocksControl 類的一個(gè)對(duì)象。MocksControl 類提供了 andReturn、andThrow、times、createMock 等方法。

MocksControl 類中包含了兩個(gè)重要的成員變量,分別是接口 IMocksBehavior 和 IMocksControlState 的實(shí)例。其中,IMocksBehavior 的實(shí)現(xiàn)類 MocksBehavior 是 EasyMock 的核心類,它保存著一個(gè) ExpectedInvocationAndResult 對(duì)象的一個(gè)列表,而 ExpectedInvocationAndResult 對(duì)象中包含著 Mock 對(duì)象方法調(diào)用和預(yù)期結(jié)果的映射。MocksBehavior 類提供了 addExpected 和 addActual 方法用于添加預(yù)期行為和實(shí)際調(diào)用。

MocksControl 類中包含的另一個(gè)成員變量是 IMocksControlState 實(shí)例。IMocksControlState 擁有兩個(gè)不同的實(shí)現(xiàn)類:RecordState 和 ReplayState。顧名思義,RecordState 是 Mock 對(duì)象在 Record 狀態(tài)時(shí)的支持類,它提供了 invoke 方法在 Record 狀態(tài)下的實(shí)現(xiàn)。此外,它還提供了 andReturn、andThrow、times 等方法的實(shí)現(xiàn)。ReplayState 是 Mock 對(duì)象在 Replay 狀態(tài)下的支持類,它提供了 invoke 方法在 Replay 狀態(tài)下的實(shí)現(xiàn)。在 ReplayState 中,andReturn、andThrow、times 等方法的實(shí)現(xiàn)都是拋出IllegalStateException,因?yàn)樵?Replay 階段,開(kāi)發(fā)人員不應(yīng)該再調(diào)用這些方法。

當(dāng)我們調(diào)用 MocksControl 的 createMock 方法時(shí),該方法首先會(huì)生成一個(gè) JavaProxyFactory 類的對(duì)象。JavaProxyFactory 是接口 IProxyFactory 的實(shí)現(xiàn)類,它的主要功能是通過(guò) java.lang.reflect.Proxy 對(duì)指定的接口創(chuàng)建動(dòng)態(tài)代理實(shí)例,也是開(kāi)發(fā)人員在外部看到的 Mock 對(duì)象。

在創(chuàng)建動(dòng)態(tài)代理的同時(shí),應(yīng)當(dāng)提供 InvocationHandler 的實(shí)現(xiàn)類。MockInvocationHandler 實(shí)現(xiàn)了這個(gè)接口,它的 invoke 方法主要的功能是根據(jù) Mock 對(duì)象狀態(tài)的不同而分別調(diào)用 RecordState 的 invoke 實(shí)現(xiàn)或是 ReplayState 的 invoke 實(shí)現(xiàn)。

創(chuàng)建 Mock 對(duì)象

下圖是創(chuàng)建 Mock 對(duì)象的時(shí)序圖:

圖5:創(chuàng)建 Mock 對(duì)象時(shí)序圖

當(dāng) EasyMock 類的 createMock 方法被調(diào)用時(shí),它首先創(chuàng)建一個(gè) MocksControl 對(duì)象,并調(diào)用該對(duì)象的 createMock 方法創(chuàng)建一個(gè) JavaProxyFactory 對(duì)象和一個(gè) MockInvocationHandler 對(duì)象。JavaProxyFactory 對(duì)象將 MockInvocationHandler 對(duì)象作為參數(shù),通過(guò) java.lang.reflect.Proxy 類的 newProxyInstance 靜態(tài)方法創(chuàng)建一個(gè)動(dòng)態(tài)代理。

記錄 Mock 對(duì)象預(yù)期行為

記錄 Mock 的預(yù)期行為可以分為兩個(gè)階段:預(yù)期方法的調(diào)用和預(yù)期輸出的設(shè)定。在外部程序中獲得的 Mock 對(duì)象,其實(shí)是由 JavaProxyFactory 創(chuàng)建的指定接口的動(dòng)態(tài)代理,所有外部程序?qū)涌诜椒ǖ恼{(diào)用,都會(huì)指向 InvocationHandler 實(shí)現(xiàn)類的 invoke 方法。在 EasyMock 中,這個(gè)實(shí)現(xiàn)類是 MockInvocationHandler。下圖是調(diào)用預(yù)期方法的時(shí)序圖:

圖6:調(diào)用預(yù)期方法時(shí)序圖

當(dāng) MockInvocationHandler 的 invoke 方法被調(diào)用時(shí),它首先通過(guò) reportLastControl 靜態(tài)方法將 Mock 對(duì)象對(duì)應(yīng)的 MocksControl 對(duì)象報(bào)告給 LastControl 類,LastControl 類將該對(duì)象保存在一個(gè) ThreadLocal 變量中。接著,MockInvocationHandler 將創(chuàng)建一個(gè) Invocation 對(duì)象,這個(gè)對(duì)象將保存預(yù)期調(diào)用的 Mock 對(duì)象、方法和預(yù)期參數(shù)。

在記錄 Mock 對(duì)象預(yù)期行為時(shí),Mock 對(duì)象的狀態(tài)是 Record 狀態(tài),因此 RecordState 對(duì)象的 invoke 方法將被調(diào)用。這個(gè)方法首先調(diào)用 LastControl 的 pullMatchers 方法獲取參數(shù)匹配器。如果您還記得自定義參數(shù)匹配器的過(guò)程,應(yīng)該能想起參數(shù)匹配器被調(diào)用時(shí)會(huì)將實(shí)現(xiàn)類的實(shí)例報(bào)告給 EasyMock,而這個(gè)實(shí)例終保存在 LastControl 中。如果沒(méi)有指定參數(shù)匹配器,默認(rèn)的匹配器將會(huì)返回給 RecordState。

根據(jù) Invocation 對(duì)象和參數(shù)匹配器,RecordState 將創(chuàng)建一個(gè) ExpectedInvocation 對(duì)象并保存下來(lái)。

在對(duì)預(yù)期方法進(jìn)行調(diào)用之后,我們可以對(duì)該方法的預(yù)期輸出進(jìn)行設(shè)定。我們以

expectLastCall().andReturn(X value).times(int times)

 

為例說(shuō)明。如果 times 方法未被顯式的調(diào)用,EasyMock 會(huì)默認(rèn)作為 times(1) 處理。下圖是設(shè)定預(yù)期輸出的時(shí)序圖:

圖7:設(shè)定預(yù)期輸出時(shí)序圖

在預(yù)期方法被調(diào)用時(shí),Mock 對(duì)象對(duì)應(yīng)的 MocksControl 對(duì)象引用已經(jīng)記錄在 LastControl 中,expectLastCall 方法通過(guò)調(diào)用 LastControl 的 lastControl 方法可以獲得這個(gè)引用。MocksControl 對(duì)象的 andReturn 方法在 Mock 對(duì)象 Record 狀態(tài)下會(huì)調(diào)用 RecordState 的 andReturn 方法,將設(shè)定的預(yù)期輸出以 Result 對(duì)象的形式記錄下來(lái),保存在 RecordState 的 lastResult 變量中。

當(dāng) MocksControl 的 times 方法被調(diào)用時(shí),它會(huì)檢查 RecordState 的 lastResult 變量是否為空。如果不為空,則將 lastResult 和預(yù)期方法被調(diào)用時(shí)創(chuàng)建的 ExpectedInvocation 對(duì)象一起,作為參數(shù)傳遞給 MocksBehavior 的 addExpected 方法。MocksBehavior 的 addExpected 方法將這些信息保存在數(shù)據(jù)列表中。

在 Replay 狀態(tài)下調(diào)用 Mock 對(duì)象方法

EasyMock 類的 replay 方法可以將 Mock 對(duì)象切換到 Replay 狀態(tài)。在 Replay 狀態(tài)下,Mock 對(duì)象將根據(jù)之前的設(shè)定返回預(yù)期輸出。下圖是 Replay 狀態(tài)下 Mock 對(duì)象方法調(diào)用的時(shí)序圖:

圖8:調(diào)用 Mock 對(duì)象方法時(shí)序圖

在 Replay 狀態(tài)下,MockInvocationHandler 會(huì)調(diào)用 ReplayState 的 invoke 方法。該方法會(huì)把 Mock 對(duì)象通過(guò) MocksBehavior 的 addActual 方法添加到實(shí)際調(diào)用列表中,該列表在 verify 方法被調(diào)用時(shí)將被用到。同時(shí),addActual 方法會(huì)根據(jù)實(shí)際方法調(diào)用與預(yù)期方法調(diào)用進(jìn)行匹配,返回對(duì)應(yīng)的 Result 對(duì)象。調(diào)用 Result 對(duì)象的 answer 方法可以獲取該方法調(diào)用的輸出。

6.使用 EasyMock 進(jìn)行單元測(cè)試小結(jié)

如果您需要在單元測(cè)試中構(gòu)建 Mock 對(duì)象來(lái)模擬協(xié)同模塊或一些復(fù)雜對(duì)象,EasyMock 是一個(gè)可以選用的框架。EasyMock 提供了簡(jiǎn)便的方法創(chuàng)建 Mock 對(duì)象:通過(guò)定義 Mock 對(duì)象的預(yù)期行為和輸出,你可以設(shè)定該 Mock 對(duì)象在實(shí)際測(cè)試中被調(diào)用方法的返回值、異常拋出和被調(diào)用次數(shù)。通過(guò)創(chuàng)建一個(gè)可以替代現(xiàn)有對(duì)象的 Mock 對(duì)象,EasyMock 使得開(kāi)發(fā)人員在測(cè)試時(shí)無(wú)需編寫自定義的 Mock 對(duì)象,從而避免了額外的編碼工作和因此引入錯(cuò)誤的機(jī)會(huì)。

上一頁(yè)12345下一頁(yè)
軟件測(cè)試工具 | 聯(lián)系我們 | 投訴建議 | 誠(chéng)聘英才 | 申請(qǐng)使用列表 | 網(wǎng)站地圖
滬ICP備07036474 2003-2017 版權(quán)所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd