Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Test-driven development improves software quality while reducing development efforts. As the foundation of an overall test strategy, unit tests must be comprehensive, easy to set up, and quick to execute. However, the dependency on the execution environment and on code outside the class under test complicates our ability to achieve these goals. Deploying the application in a container significantly slows down the code-and-test cycle. Furthermore, the need to collaborate with other classes usually leads to more complex test setups and slower test runs.
Integrating two popular test frameworks, StrutsTestCase and EasyMock, to unit-test Struts applications leads to easier test setups and faster test runs. However, these two frameworks leave a gap that prevents an ideal integration. In this article, I examine both an object-oriented solution and an aspect-oriented solution to this problem. The comparison also demonstrates how aspect-oriented programming (AOP) complements object-oriented programming (OOP) by simplifying the solution to a seemingly difficult problem.
Non-trivial Struts applications exhibit both execution environment and class dependencies because Struts actions execute in
a servlet container and typically call other classes to process the requests. The mock object testing approach helps remove
the unwanted dependencies. The StrutsTestCase testing framework provides a mock implementation of the servlet container with
MockStrutsTestCase class that extends the base JUnit test case. It facilitates out-container testing that speeds up unit testing cycles. EasyMock,
another testing framework, makes it easy to dynamically mock the collaborating classes. The mocks substitute real classes
with simpler implementations and add verification logic to support unit testing.
Clearly, it is advantageous to combine the two frameworks so that Struts applications can be tested in true isolation. Ideally, you want to implement such a unit test with the following steps:
MockStrutsTestCaseso that it simulates the servlet container.
Step 4 performs dependency injection that steers the Struts action under test away from its real collaborator to interact
with the mocked one. To inject the mock generated by EasyMock into actions, you need access to action instances in the test
classes. Unfortunately, this presents an obstacle, as access is not easily obtained from
How can you access the action instances from
MockStrutsTestCase? Let's look at the relationships between
MockStrutsTestCase and the controller components of Struts.
Figure 1 highlights the key relationships that could potentially lead to a solution.
Figure 1. Relationships that could lead to an OOP solution. Click on thumbnail to view full-sized image.
MockStrutsTestCasehas a public getter method for retrieving
ActionServlethas a protected getter method for
RequestProcessorstores the action instances as a protected member.
Can you subclass both
RequestProcessor to provide
MockStrutsTestCase the access to the actions? The resulting call chain would be:
This approach doesn't work when you look at the sequence of calls that link
MockStrutsTestCase to Struts actions.
Figure 2 illustrates the key interactions between
MockStrutsTestCase and Struts components.
Figure 2. Interactions between MockStrutsTestCase and Struts components. Click on thumbnail to view full-sized image.
The problem, as shown in Figure 2, involves the timing of Struts action creation. Mock injection into the actions needs to
happen before the call to
MockStrutsTestCase.actionPerform(). However, the actions are not yet available because only after the call to
RequestProcessor create the action instances.
Since you cannot easily propagate the action instance to
MockStrutsTestCase, why not just subclass
RequestProcessor and override the
processActionCreate() method? In the overridden method, you have access to all the action instances, so creating, configuring, and setting a mock
to the right action instance becomes straightforward. Because
MockControl.verify() should be called after
actionPerform(), you also need to override
processActionPerform() to make the verification call.
This solution is impractical for testing non-trivial Struts applications. Even if all actions interact with a single mock,
testing one action would likely require multiple test methods, each with different mock expectations. The proposed solution
would end up creating different
RequestProcessor subclasses, each setting different mock expectations. Multiple Struts configuration files are also needed to specify the
RequestProcessor subclasses. Managing a large number of tests would become a headache.
Thus, somehow making the action instance available to
MockStrutsTestCase before the action executes is still desirable. If you are familiar with AOP, you recognize the simple solution that directly
maps to this requirement. The key is to define a pointcut that captures the action execution join point and then a before advice to inject the mock into the action.
Here, I chose AspectJ to implement the solution. Other AOP implementations such as Spring AOP should work as well. Spring AOP would require one extra step that delegates Struts action management to Spring with Spring's
Figure 3 shows the static model of the unit test example with the AOP-based solution.
Figure 3. Static model of the unit test example with the AOP-based solution. Click on thumbnail to view full-sized image.
SimpleAction is a subclass of a Struts action and collaborates with
SimpleActionTest derives from
MockStrutsTestCase to test
SimpleActionTest creates and sets up a mock
ActionService using EasyMock.
SimpleActionTest also implements the
StrutsActionPreExecuteListener interface to receive notification when
SimpleAction's execute method is about to run. As part of the notification,
SimpleActionTest receives the
SimpleAction instance to inject the
ActionService mock. It is the aspect class
StrutsActionPreExecuteNotifier that notifies any test class that implements the listener interface and makes the action instance available.
|Forum migration complete By Athen|
|Forum migration update By Athen|