不利的一面是,这种技术在逻辑难于抽取时无能为力了。例如,好让简 单 的逻辑留在原处。同时,一些方面将状态存储到本地或者它们建议的类的 ITD 中 。状态存储通常构成了方面逻辑的签名部分,它并不总能干净地转移到 helper 类中。
模式 2. 使用模拟对象记录建议触发
针对 :横切规范和功能
概述 :这项技术补充了前一项技术。如果将建议行为抽取到另一个类中,那 么可以用一个 mock 对象替代 helper 类对象,并验证建议是否在正确的联结 点上触发。还可以验证建议将正确的上下文传递给了 helper 类,不管是直接用 建议参数还是用之前存储的状态。
注: 如果需要对 mock 对象的介绍,请参阅 参考资料。
例子:用一个 mock HighlightUtil 测试 Highlighter 方面
我们已经看到了方面如何委派到另一个类中以处理实际的文字突出显示。这 使 得在测试中向方面注入不同的 highlighter 实现成为可能。清单 3 中的代码利 用 JMock 库做到了这一点。(请参阅 参考资料。)
清单 3. 用 JMock 测试来自方面的调用
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() 方法实例化 mock 对象并将它注入到方面中。测试方法告诉 mock 等待对名为 “highlight” 的方法的调用,这个方法有两个参数:getTitle() 的返回值和在 SearchResult 中存储的单词清单。设置了期望后,测试调用 getTitle() 方法,它应当触发方面并产生预期的对 mock 的调用。如果 mock 没有收到调用,那么它会在销毁时自动使测试失败。
注意 setUp() 方法存储了到原来 HighlightUtil 的引用。这是因为方面像 大多数对象一样,是单元素(singleton)的。因此,销毁时撤销 mock 注入的 影响很重要,否则,mock 会持续留在方面中并影响其他测试。这个方面的正确 销毁如下所示:
@Override
protected void tearDown() throws Exception {
try {
HighlightResults.aspectOf().setHighlightUtil(original);
} finally {
super.tearDown();
}
}
优缺点
这个模式对前一个模式做了补充,只是它测试方面的横切规范和上下文处理 而不是横切行为。因为不用检查方面的输出的间接副作用,所以可以更容易地产 生联结点匹配和上下文传递行为中的临界用例。
重要的是要认识到委派逻辑的优缺点,用 mock 进行测试对于使用对象或方 面的技术都是类似的。在这两种情况下,都是分离关注点,然后以更隔离的方法 验证每一个关注点。
对于注入 mock 来说,有一个特定于方面的问题。如果使用单元素方面(默 认的),那么对于方面的字段所做的所有改变,如用 mock 替换一个字段,在测 试结束时都必须撤销。(否则,mock 会挂起并可能影响系统的其他部分。)这 种销毁逻辑很难实现和记忆。编写一个测试清理方面,自动在每次测试后像在例 子中那样重新设置方面从概念上来说是简单的,但是其细节超出了本文的范围。
IV. 使用 mock 目标
在后一节中,我介绍了我自己发明的、用于描述在编写方面测试时用到的 一种测试 helper 类类型的术语:mock 目标。在方面之前的世界中,一个 mock 对象 表示一个(手写或者动态生成)的类,它模仿要测试的一些类的协作器。 与此类似,mock 目标 是一个模仿要测试的一些方面的合法建议目标的类。
为了创建 mock 目标,编写一个与生产中的建议有某些相似结构或者行为的 类。例如,如果对于由 getter 返回的文字的突出显示感兴趣,可以编写下面这 样的一个 mock 目标:
//an inner class of the enclosing test case
public class HighlightMockTarget implements Highlightable {
public String getSomeString() {
return "I am a big bear!";
}
}