Earlier this year I was giving a lecture on J2EE technologies to a group of graduate students at North Carolina State University. I presented a sample design that illustrated the use of servlets and JSP technology, explaining how the MVC approach could be combined with a simple data access object (DAO) to handle queries and updates of persistent data in an application. As I presented the material I saw knowing smiles and nodding heads in the audience. After the presentation, however, I received an e-mail from a student. Why had I implemented not just one, but two DAOs, and why had I set up a Factory class to allow a choice between them?
This was puzzling, but I soon realized the problem: I had neglected to tell the class that I was implementing a third pattern in addition to MVC and DAO. In particular, my design employed mock objects to make testing easier. Mock objects are so fundamental to the way that I think about DAOs that I couldn't imagine implementing one without the other. At an even deeper level, I had automatically adjusted my design to allow for easier testing, a concept that is as unfamiliar to many experienced developers as it was to my student.
In this article, you'll learn about layered testing with simulated data access objects (SDAOs). Like mock objects, SDAOs replace existing objects with ones developed specifically for testing. Unlike mock objects, however, SDAOs needn't be instrumented to include test assertions. The goal of the SDAO pattern is to allow you to isolate one layer (the domain object layer) from another (the data access layer). We'll begin with a review of the Data Access Object pattern.
The goal of the Data Access Object pattern is to provide a single point of contact to a particular data source. Like many design patterns, DAO is grounded on the separation of design tasks. Specifically, DAO separates business logic from database persistence code. Data access objects are responsible for operating on objects that hold data stored in a relational database. The objects that DAOs operate on are usually called value objects, although the term data transfer objects (DTO) is more meaningful.
A DAO class typically contains CRUD methods -- that is create(), read(), update(), and delete() -- that operate on its DTOs. For instance, let's assume that we're building a system to register conference attendees. Our DAO class interface might look like the one shown in Listing 1:
public interface AttendeeDAO {
public void createAttendee(Attendee person) throws DAOException;
public void updateAttendee(Attendee person) throws DAOException;
public Collection getAllAttendees() throws DAOException;
public void deleteAttendee(Attendee person) throws DAOException;
public Attendee findAttendeeForPrimaryKey(int primaryKey)
throws DAOException;
|
Declaring this interface (or one like it) is a standard part of implementing the DAO pattern. A concrete DAO application would implement the interface as shown in Figure 1:
Figure 1. Example DAO application

In most cases, a DAO application like the one above would use JDBC for database access. In the case of the getAllAttendees() method, for instance, the class would obtain a connection to a database, query the database for the list of rows in the Attendee table, iterate through the ResultSet returned from the query, and then construct Attendee objects for each of the rows in the ResultSet.
See Resources for further discussion and examples of the DAO pattern.
Simulated data access objects essentially simulate a back-end datastore. Implementing the SDAO pattern enables us to test various application layers (such as business logic and GUI) without having a real database in place.
Here are some of the concrete benefits of layered testing with SDAO:
-
It's cheap: Using a simulated database for testing and debugging saves you the cost of putting DB2 (for example) on every developer's desktop.
-
It's easy: Building an application with servlets, JSP files, and EJB components is complex enough without having to deal with database errors, too. Layered testing allows you to work out your presentation and business logic without simultaneously worrying about the back-end database.
-
It's fast: Separation by layer lets you isolate problems as they occur, which results in faster debug cycles.
Some errors (such as
TransactionRollbackException) are difficult to locate; removing the database layer from the equation lets you hone in on the actual problem more quickly.
- It's flexible: SDAOs can come in handy for performance profiling and testing. While some classes of performance problem (such as database deadlock) require an actual database to resolve, SDAOs let you obtain measurements of only domain and GUI performance, which you can then use to resolve problems at those layers.
The best way to understand how SDAO works is to see it in action, and hopefully apply it for yourself. We'll start with a simple example of a simulated data access object, shown in Listing 2:
public class DefaultDAO implements AttendeeDAO {
private static DefaultDAO instance = new DefaultDAO();
private Vector attendees = new Vector();
/**
* @see AttendeeDAO#createAttendee(Attendee)
*/
public void createAttendee(Attendee person) throws DAOException {
getAllAttendees().add(person);
}
/**
* @see AttendeeDAO#updateAttendee(Attendee)
*/
public void updateAttendee(Attendee person) throws DAOException {
Attendee match =
findAttendeeForPrimaryKey(person.getAttendeeKey());
attendees.remove(match);
attendees.add(person);
}
/**
* @see AttendeeDAO#getAllAttendees()
*/
public Collection getAllAttendees() throws DAOException {
return getAttendees();
}
/**
* @see AttendeeDAO#deleteAttendee(Attendee)
*/
public void deleteAttendee(Attendee person) throws DAOException {
Attendee match =
findAttendeeForPrimaryKey(person.getAttendeeKey());
attendees.remove(match);
attendees.add(person);
}
/**
* Gets the attendees
* @return Returns a Vector
*/
public Vector getAttendees() {
return attendees;
}
/**
* Sets the attendees
* @param attendees The attendees to set
*/
public void setAttendees(Vector attendees) {
this.attendees = attendees;
}
/**
* Gets the instance
* @return Returns a DefaultDAO
*/
public static DefaultDAO getInstance() {
return instance;
}
/**
* Sets the instance
* @param instance The instance to set
*/
public static void setInstance(DefaultDAO anInstance) {
instance = anInstance;
}
public Attendee findAttendeeForPrimaryKey(int primaryKey)
throws DAOException {
Enumeration enum = attendees.elements();
while (enum.hasMoreElements()) {
Attendee current = (Attendee) enum.nextElement();
if (current.getAttendeeKey() == primaryKey)
return current;
}
throw new DAOException("Primary Key not found "
+ primaryKey);
}
}
|
Now, that's hardly rocket science, is it? The DefaultDAO class stores an instance of itself (as a Singleton) in the static variable instance, allowing access to that instance through the getInstance() method. Users of the class can then add and remove Attendee elements from the collection the
Singleton instance holds, or replace the elements in the collection.
To make this work in real life, we need to be able to replace the "real" DAO class with the new "simulated" DAO class in our program. Our client code cannot itself reference either the Db2AttendeeDAO class or the DefaultDAO class. So, we use a Factory class (a.k.a. an object factory) to provide the client code with instances of Db2AttendeeDAO and DefaultDAO as needed.
Our object factory is fairly simple. It returns only two class instances, using a kind of software "switch" (shown in the getAttendeeDAO() method) to toggle between them. This switch can also check the value of a System property, or examine some other global value, as shown in Listing 3:
public class AttendeeDAOFactory {
public static AttendeeDAO getAttendeeDAO() {
String mode = (String) System.getProperty("TestMode");
if ("Simulated".equals(mode))
return DefaultDAO.getInstance();
else
return new DbAttendeeDAO();
}
}
|
When you run your tests you will usually begin by setting the Factory switch to return the simulated class. Doing so ensures that you can test the remaining layers of the system in isolation from the database. Only in later tests would you ever set the switch to return the "real" DAO. Figure 2 gives you an idea of what the final design, including the Factory class, might look like:
Figure 2. Example SDAO implementation

Once you understand the basic SDAO implementation, you can explore other ways of working with the simulated DAO (DefaultDAO) class. So far, you've seen only the simplest implementation, where results are fetched from an in-memory collection that must be populated during the test. A common extension of this idea is to prefill the collection with default values in the constructor of the class. The major disadvantage of using a Singleton, as we did here, is that you must clear the Singleton between each test. If you miss it in one test, then it can cause a failure in a later test. Luckily most unit test frameworks (such as JUnit) provide facilities to make this type of testing easy. For instance, in JUnit, you can put code to clear the Singleton in the teardown() method of your test class, and put any prefill code in the setUp() method of the test class.
A second approach, which is slightly more complicated but also provides for more realistic tests, is to use Java Serialization or XML to read a set of objects from a file. Either technology will let you use several files to represent different initial conditions for the same test.
I often take a two-fold approach to SDAO testing. The first DAO I build is the "default" DAO; it goes to an in-memory collection of DTOs, then gets passed off to the team building the upper layers of the application (Servlets and JSP files, for instance). I then work with a second team to build the DAOs that will actually work with the database. This approach lets both teams work simultaneously, with their interaction defined by the shared contract of the DAO interface.
In the IBM Software Services for WebSphere group, we've successfully applied the layered testing techniques described here to dozens of customer engagements. In addition to improving our overall product, SDAO has been a crucial part of helping our teams master the peculiarities of the various J2EE APIs. Using both simulated and actual DAOs lets us work simultaneously on many layers of an application, without getting overwhelmed by the complexity of putting everything together at once.
The author would like to thank Stacy Joines and Ken Hygh for their helpful suggestions on this article.
- Curious about design patterns? It all started with the Gang of Four's Design Patterns: Elements of Reusable Object Oriented Software (Erich Gamma, Richard Helms, Ralph Johnson, John Vlissides; Addison-Wesley, 1995).
- In addition to Design Patterns, Core J2EE Patterns: Best Practices and Design Strategies (Deepak Alur, John Crupi, Dan Malks; Addison-Wesley, 2001) is essential reading for J2EE developers.
- Martin Fowler's Patterns of
Enterprise Application Architecture (Addison-Wesley, 2002) is another comprehensive introduction to patterns for enterprise development.
- You may also want to check out Paul Monday's "Java design patterns 201" (developerWorks, April 2002), an advanced tutorial for the design pattern aficionado.
- Learn a little more about some of the enterprise programming technologies we briefly touched on here, with Kyle Gabhart's J2EE pathfinder series (developerWorks).
- "Endo-Testing: Unit Testing with Mock Objects" (Tim Mackinnon, Steve Freeman, Philip Craig; XP2000) is a great introduction to unit testing and mock objects -- and it was the first paper on the subject.
- Sourceforge currently hosts Kent Beck and Erich Gamma's introduction to unit testing with JUnit, "Test Infected: Programmers Love Writing Tests" (Java Report, July 1998).
- Alexander Day Chaffee and William Pietri's "Unit testing with mock objects" (developerWorks, November 2002) shows you how to use the Factory pattern to enhance testing with mock objects.
- You can combine mock object testing with a variety of techniques and technologies.
Nicholas Lesiecki's "Test flexibly with AspectJ and mock objects" (developerWorks, May 2002) shows you how mock objects and aspect-oriented programming can work together.
- You'll find hundreds of articles about every aspect of Java programming in the
developerWorks Java technology zone.
Kyle Brown is a senior technical staff member with IBM Software Services for WebSphere. Kyle provides consulting services, education, and mentoring on object-oriented topics and Java 2 Enterprise Edition (J2EE) technologies to Fortune 500 clients. He is co-author of Enterprise Java Programming with IBM WebSphere, WebSphere 4.0 AEs Workbook for Enterprise JavaBeans (3rd Edition), and The Design Patterns Smalltalk Companion. He is also a frequent conference speaker on Enterprise Java, OO design, and design patterns. You can reach him at brownkyl@us.ibm.com.
Comments (Undergoing maintenance)





