四、AOP方案
因此,我们非常希望,在执行某行为之前能够通过某种方式实现在MockStrutsTestCase中使用该行为的实例。如果你熟悉AOP,那么,你会立即意识到它所提供的简单方案即能直接满足这一要求。注意:这里的关键是定义一个切点,由它负责捕获行为执行连接点;然后通过一个before advice把模拟注入到相应的行为中。
在此,我选择使用AspectJ框架来实现这一方案。当然,其它的例如Spring AOP这样的AOP实现也应该能够良好工作。不过,Spring AOP还需要一个额外的步骤—通过Spring框架中的DelegatingActionProxy类把对Struts行为的管理委托给Spring。
图3展示了基于AOP方案的单元测试示例静态模型。
图3:基于AOP方案的单元测试示例静态模型
SimpleAction是一个Struts行为的子类,同时与ActionService进行协作。其中,SimpleActionTest派生于MockStrutsTestCase,用来测试SimpleAction。
SimpleActionTest使用EasyMock创建和建立一个模拟ActionService。SimpleActionTest还实现StrutsActionPreExecuteListener接口以便在即将运行 SimpleAction的execute方法时接收通知。作为通知的一部分,SimpleActionTest接收SimpleAction实例以便注入ActionService模拟。由方面类StrutsActionPreExecuteNotifier负责通知任何实现监听器接口的测试类,并且使相应的行为实例可用。
下面的步骤描述了实现StrutsActionPreExecuteNotifier的过程:
◆首先,由一个切点选择相应的测试方法执行连接点。另一方面,这个测试方法驻留于负责监听该行为的预执行事件的测试类中。另外,这个切点还会暴露当前执行的测试类对象: pointcut mockStrutsTest(StrutsActionPreExecuteListener actionTest):
◆然后,由第二个切点负责捕获上面的行为执行连接点。通过结合第一个切点,匹配范围被限制到该行为相应的测试方法的调用流程的内部。这种进一步缩小的范围对行为执行(并非通过测试方法激活)起到过滤作用。最终,方面根本不会影响到最后生成的代码。该行为及其相应的测试类实例都是经由切点参数加以暴露的: pointcut strutsActionExecute(Action action, StrutsActionPreExecuteListener actionTest):
◆最后,由一个与前一个切点相关联的before advice负责通知测试类(它们担任行为事件的监听器)并且传递相应于模拟注入的行为实例:
图4展示了这些类之间的动态交互情形。
图4:类之间的动态交互
注意,图中从行为到方面的虚线描述了对行为执行连接点的捕获情况。此时序图与第一个时序图比较,其重要区别正在于行为执行之前发生的三个步骤:
1.一个切点捕获行为执行连接点(由从SimpleAction指向StrutsActionPreExecuteNotifier的虚线箭头指出)。
2.方面的before advice负责通知测试类并且把相应的行为实例传递给它。
3.测试类把模拟对象注入到即将要开始执行的行为实例中。