Test Flexibly With AspectJ And Mock Objects
From NeoWiki
- Enhance unit testing with test-only behavior
Nicholas Lesiecki, Principal software engineer, eBlox, Inc.
01 May 2002
- Programmers who have incorporated unit testing into their development process know the advantages it brings: cleaner code, courage to refactor, and higher velocity. But even the most die-hard unit testers can falter when faced with testing a class that relies on system state for its behavior. Nicholas Lesiecki, a respected Java programmer and leader in the XP community, introduces the problems surrounding test-case isolation and shows us how to use mock objects and AspectJ to develop precise and robust unit tests.
The recent attention to Extreme Programming (XP) has spilled over onto one of its most portable practices: unit testing and test-first design. As software shops have adopted XP's practices, many developers have seen the increase in quality and speed that comes from having a comprehensive unit-test suite. But writing good unit tests takes time and effort. Because each unit cooperates with others, writing a unit test can involve a significant amount of setup code. This makes tests more expensive, and in certain cases (such as code that acts as a client to a remote system) such tests can be almost impossible to implement.
In XP, unit tests complement integration and acceptance tests. These latter two test types may be undertaken by a separate team or as a separate activity. but unit tests are written simultaneously with the code to be tested. Facing the pressure of an impending deadline and a headache-inducing unit test, it is tempting to write a haphazard test or not bother with the test at all. Because XP relies on positive motivation and self-sustaining practices, it's in the best interest of the XP process (and the project!) to keep the tests focused and easy to write.
Tip: Required background This article focuses on unit testing with AspectJ, so the article assumes you are familiar with basic unit testing techniques. If you aren't familiar with AspectJ, it would probably help to read my introduction to AspectJ before you go any further (see Resources). The AspectJ techniques presented here aren't very complex, but aspect-oriented programming requires a little getting used to. In order to run the examples, you will need to have Ant installed on your test machine. You do not, however, need any special Ant expertise (beyond what's required for a basic install) to work with the examples. For more information or to download Ant, see the Resources section. |
Mock objects can help to solve this dilemma. Mock object tests replace domain dependencies with mock implementations used only for testing. This strategy does, however, present a technical challenge in certain situations, such as unit testing on remote systems. AspectJ, an aspect-oriented extension to the Java language, can take unit testing the rest of the way by allowing us to substitute test-only behavior in areas where traditional object-oriented techniques would fail.
In this article we'll examine a common situation where writing unit tests is both difficult and desirable. We'll start by running a unit test for the client component of an EJB-based application. We'll use the example as a springboard to discuss some of the problems that can arise with unit testing on remote client objects. To solve these problems, we'll develop two new test configurations that rely on AspectJ and mock objects. By the end of the article you should have an appreciation of common unit-testing problems and their solutions, as well as a window into some of the interesting possibilities afforded by AspectJ and mock object testing.
In order to follow the code samples we'll be working with throughout this article, you might want to install the example application now.
A unit testing example
The example consists of a test for an EJB client. Many of the issues raised in this case study could also be applied to code that calls Web services, JDBC, or even a "remote" part of the local application through a facade.
The server-side CustomerManager EJB performs two functions: it looks up the names of customers and registers new customer names with the remote system. Listing 1 shows the interface that CustomerManager exposes to its clients:
Listing 1. CustomerManager's remote interface
public interface CustomerManager extends EJBObject { /** * Returns a String[] representing the names of customers in the system * over a certain age. */ public String[] getCustomersOver(int ageInYears) throws RemoteException; /** * Registers a new customer with the system. If the customer already * exists within the system, this method throws a NameExistsException. */ public void register(String name) throws RemoteException, NameExistsException; }
The client code, called ClientBean, essentially exposes the same methods, delegating their implementation to the CustomerManager, as shown in Listing 2.