不利的一面是,這種技術(shù)在邏輯難于抽取時(shí)無能為力了。例如,好讓簡 單 的邏輯留在原處。同時(shí),一些方面將狀態(tài)存儲(chǔ)到本地或者它們建議的類的 ITD 中 。狀態(tài)存儲(chǔ)通常構(gòu)成了方面邏輯的簽名部分,它并不總能干凈地轉(zhuǎn)移到 helper 類中。
模式 2. 使用模擬對象記錄建議觸發(fā)
針對 :橫切規(guī)范和功能
概述 :這項(xiàng)技術(shù)補(bǔ)充了前一項(xiàng)技術(shù)。如果將建議行為抽取到另一個(gè)類中,那 么可以用一個(gè) mock 對象替代 helper 類對象,并驗(yàn)證建議是否在正確的聯(lián)結(jié) 點(diǎn)上觸發(fā)。還可以驗(yàn)證建議將正確的上下文傳遞給了 helper 類,不管是直接用 建議參數(shù)還是用之前存儲(chǔ)的狀態(tài)。
注: 如果需要對 mock 對象的介紹,請參閱 參考資料。
例子:用一個(gè) mock HighlightUtil 測試 Highlighter 方面
我們已經(jīng)看到了方面如何委派到另一個(gè)類中以處理實(shí)際的文字突出顯示。這 使 得在測試中向方面注入不同的 highlighter 實(shí)現(xiàn)成為可能。清單 3 中的代碼利 用 JMock 庫做到了這一點(diǎn)。(請參閱 參考資料。)
清單 3. 用 JMock 測試來自方面的調(diào)用
public class DelegatedHighlightingUnitTest extends MockObjectTestCase {
Collection words;
private HighlightUtil original;
private SearchResult result;
private Mock mockUtil;
public void setUp() throws Exception {
super.setUp();
setUpMockHighlightUtil();
words = Collections.singleton("big");
result = new SearchResult();
result.setTitle("I am a big bear!");
result.setHighlightedWords(words);
}
private void setUpMockHighlightUtil() {
original = HighlightResults.aspectOf().getHighlightUtil();
mockUtil = mock(HighlightUtil.class);
HighlightResults.aspectOf().setHighlightUtil((HighlightUtil) mockUtil.proxy());
}
public void testHighlightUtilAppliedToTitleOfSearchResult() {
mockUtil.expects(once())
.method("highlight")
.with(eq("I am a big bear!"), eq(words));
result.getTitle();
}
}
setUp() 方法實(shí)例化 mock 對象并將它注入到方面中。測試方法告訴 mock 等待對名為 “highlight” 的方法的調(diào)用,這個(gè)方法有兩個(gè)參數(shù):getTitle() 的返回值和在 SearchResult 中存儲(chǔ)的單詞清單。設(shè)置了期望后,測試調(diào)用 getTitle() 方法,它應(yīng)當(dāng)觸發(fā)方面并產(chǎn)生預(yù)期的對 mock 的調(diào)用。如果 mock 沒有收到調(diào)用,那么它會(huì)在銷毀時(shí)自動(dòng)使測試失敗。
注意 setUp() 方法存儲(chǔ)了到原來 HighlightUtil 的引用。這是因?yàn)榉矫嫦?大多數(shù)對象一樣,是單元素(singleton)的。因此,銷毀時(shí)撤銷 mock 注入的 影響很重要,否則,mock 會(huì)持續(xù)留在方面中并影響其他測試。這個(gè)方面的正確 銷毀如下所示:
@Override
protected void tearDown() throws Exception {
try {
HighlightResults.aspectOf().setHighlightUtil(original);
} finally {
super.tearDown();
}
}
優(yōu)缺點(diǎn)
這個(gè)模式對前一個(gè)模式做了補(bǔ)充,只是它測試方面的橫切規(guī)范和上下文處理 而不是橫切行為。因?yàn)椴挥脵z查方面的輸出的間接副作用,所以可以更容易地產(chǎn) 生聯(lián)結(jié)點(diǎn)匹配和上下文傳遞行為中的臨界用例。
重要的是要認(rèn)識(shí)到委派邏輯的優(yōu)缺點(diǎn),用 mock 進(jìn)行測試對于使用對象或方 面的技術(shù)都是類似的。在這兩種情況下,都是分離關(guān)注點(diǎn),然后以更隔離的方法 驗(yàn)證每一個(gè)關(guān)注點(diǎn)。
對于注入 mock 來說,有一個(gè)特定于方面的問題。如果使用單元素方面(默 認(rèn)的),那么對于方面的字段所做的所有改變,如用 mock 替換一個(gè)字段,在測 試結(jié)束時(shí)都必須撤銷。(否則,mock 會(huì)掛起并可能影響系統(tǒng)的其他部分。)這 種銷毀邏輯很難實(shí)現(xiàn)和記憶。編寫一個(gè)測試清理方面,自動(dòng)在每次測試后像在例 子中那樣重新設(shè)置方面從概念上來說是簡單的,但是其細(xì)節(jié)超出了本文的范圍。
IV. 使用 mock 目標(biāo)
在后一節(jié)中,我介紹了我自己發(fā)明的、用于描述在編寫方面測試時(shí)用到的 一種測試 helper 類類型的術(shù)語:mock 目標(biāo)。在方面之前的世界中,一個(gè) mock 對象 表示一個(gè)(手寫或者動(dòng)態(tài)生成)的類,它模仿要測試的一些類的協(xié)作器。 與此類似,mock 目標(biāo) 是一個(gè)模仿要測試的一些方面的合法建議目標(biāo)的類。
為了創(chuàng)建 mock 目標(biāo),編寫一個(gè)與生產(chǎn)中的建議有某些相似結(jié)構(gòu)或者行為的 類。例如,如果對于由 getter 返回的文字的突出顯示感興趣,可以編寫下面這 樣的一個(gè) mock 目標(biāo):
//an inner class of the enclosing test case
public class HighlightMockTarget implements Highlightable {
public String getSomeString() {
return "I am a big bear!";
}
}