简介:AOP 使编写特定于应用程序的横切关注点测试比任何时候都要容易了 。 要了解其原因以及如何实现,请听 Nicholas Lesiecki 介绍测试面向方面的代 码 所具有的好处,并展示在 AspectJ 中测试横切行为的样式。
在过去五年中广泛采用的程序员测试是由显著的生产率和得到的代码质量所 驱 动的。不过,在面向方面编程(AOP)出现之前,为横切行为(如安全、事务管 理 或者持久性)编写测试很困难。为什么呢?因为这些行为没有很好地模块化。如 果没有可测试的单元,那么很难编写出单元测试。随着 AOP 的普及,编写与 横 切关注点在目标系统中的实现无关的、对其进行检查的测试已变得可行并且值得 去做。
在本文中,我将介绍测试用方面实现的横切行为的一组技术。重点放在方面 的 单元测试,但是我也展示了有助于建立对面向方面应用程序的信任度的其他模式 。很快您会看到,测试方面涉及许多与测试对象相同的技巧和概念,并且有许 多相同的做法和设计好处。
本文的撰写基于我在 AspectJ 中的经验。许多概念应当可以移植到其他 AOP 实现中,但是有些概念是特定于语言的。请参阅 下载 以下载本文源代码,请参 阅 参考资料 以下载完成例子所需要的 Aspectj 和 AJDT。
面向方面代码的单元测试
应用程序的好的自动测试集应当像图 1 那样:以隔离的、对各个类的测试构 成一个广泛的基础,使测试覆盖面广,并能迅速分离出错误。在这之上是集成的 、端到端的系统测试,它验证各单元是否可以协调工作。如果这些层是良构的并 且频繁运行,那么它们结合在一起可以增加对于应用程序行为的信任度。
在金字塔底部的单元测试很重要,这有几个理由。首先,它们帮助您产生那 些 在集成测试中难于再现或者需要繁琐步骤才能再现的关注点用例。其次,由于它 们涉及的代码更少,因此它们运行起来通常更快(因此可以更经常地运行它们) 。第三,它们可以帮助您思考每个单元的接口和要求。好的单元测试要求单元间 的松散耦合,这是在测试条件下让测试得以运行的条件。
图 1. 分层的测试
但是横切行为会怎么样呢?想像一位客户的要求:“在执行对 ATM 类的任何 操作之前要检查调用者的安全凭证。”当然可以(并且应该)针对这项要求编写 集成测试。不过,非面向对象的开发环境使得编写“在操作之前检查安全性”这 种行为的单元测试很难将这种行为分离出来。这是因为行为混入到了目标系统中 ,让人很难把握或者用工具分析。但是如果用方面开发,那么可以将这些行为 表示为建议(advice),应用到所有匹配某个切点(pointcut)的操作。现在行 为可以很好地用单元表示,可以在隔离的情况下测试或者在 IDE 显示它。
面向方面的代码在哪里出现问题了
在讨论对方面进行单元测试的技术之前,我要简单讨论错误类型。横切行为 分 为两个主要部分:行为做什么(我称之为横切功能)和行为用在什么地方(我称 之为横切规范)。回到 ATM 的例子,横切功能检查调用者的安全凭证。横切规 范 检查 ATM 类中每一个公共方法。
为了实现的真正信任度,需要同时检查功能和规范(或者不严格地说,建议 和 切点)。在完成这个例子的过程中,我将强调给定的测试模式是验证横切功能、 规范,还是两者同时验证。
注意我将重点放在测试切点、建议和支持它们的代码上。类型间声明(和其 他 方面功能)当然是可测试的。我在本文中展示的一些技巧稍加修改可以对它们 使用。它们还有自己的一组技巧,其中许多是很直观的。不过为了节省篇幅,我 决定不在本文中明确地讨论它们。
测试模式编目
我将本文写成测试面向方面代码的模式的编目。对于每种模式,我描述了它 针 对哪种类型的错误,对该模式进行总结,提供一个例子并讨论该模式的优缺点。 编目分为四节:
测试完整的单元 :此节展示了测试完整的系统部分(换句话说,同时测试方 面和非方面类)的模式。这种技术是在没有使用方面时获得横切行为信任度的惟 一方法,并在使用方面时仍然是一种重要的工具。
使用可视化工具 :这里描述的两种模式利用了 AspectJ IDE 对 Eclipse 的 支持,也称为 AJDT。严格地讲,使用可视化工具检查应用程序的横切结构不是 一 种测试技术。不过,它可帮助您理解并获得对应用程序的横切关注点的信任。
使用委派 :此节展示可以帮助将前面提到的两类错误分开的两种模式。通过 将一些逻辑从建议中分离到 helper 类中(或者方法中),可以编写与横切规范 无关的、检查应用程序横切行为的测试。
使用 mock 目标 :后一节包括三种模式,它们使用模拟真实建议目标的 “mock 目标”类,可以在不将方面集成到真正目标的条件下测试联结点匹配和 建 议行为。
Highlighter 方面
为了展示这个编目中的样式,我使用了一个实现突出显示搜索术语(即在搜 索 结果中突出显示用户的查询术语)的方面。我实现了与在上一例子中展示的方面 非常相像的一个方面。该系统要在结果汇总页、细节页和应用程序的其他一些地 方中突出显示术语。我在本文中展示的这个例子只横切一个类,但是原理是一样 的。清单 1 包含 Highlighter 方面的一个实现:
清单 1. Highlighter 定义了突出显示行为
public aspect Highlighter{ /* ITDs to manage highlighted words */ private Collection Highlightable.highlightedWords;
public Collection Highlightable.getHighlightedWords() { return highlightedWords; } public void Highlightable.setHighlightedWords(Collection highlightedWords){ this.highlightedWords = highlightedWords; }
public pointcut highlightedTextProperties() : ( execution(public String getProduct()) || execution(public String getTitle()) || execution(public String getSummary()) );
String around(Highlightable highlightable) : highlightedTextProperties() && this(highlightable) { String highlighted = proceed(highlightable); for (String word : highlightable.getHighlightedWords()) { Pattern pattern = patternForWord(word); Matcher matcher = pattern.matcher (highlighted); highlighted = matcher.replaceAll(" "bold">$0"); } return highlighted; }
private Pattern patternForWord (String word) { return Pattern.compile("\b\Q" + word + "\E\b", Pattern.CASE_INSENSITIVE); } }