Skip to main content

A stepped approach to J2EE testing with SDAO

Use simulated and actual data access objects to effectively and efficiently test your J2EE apps

Kyle Brown (brownkyl@us.ibm.com), Senior Technical Staff Member, IBM
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.

Summary:  The Data Access Object pattern has become a standard part of the J2EE developer's arsenal. What most developers don't know is that one of its variations allows for much easier testing. Simulated data access objects bring together the best of DAO, mock objects, and layered testing, letting you simultaneously improve both your testing results and your overall development method. Enterprise Java developer (and resident SDAO guru) Kyle Brown uses code samples and discussion to guide you through the concepts and everyday use of SDAO.

Date:  04 Mar 2003
Level:  Intermediate
Activity:  2282 views

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.

A word about patterns

Read more about how patterns give developers a shared design vocabulary.

Data access objects

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
Diagram of an example DAO application

Message handling

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

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.

SDAO in action

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.


The object factory

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
Diagram of an SDAO implementation

Advanced SDAO

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.


Conclusion

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.


Resources

About the author

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)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=10757
ArticleTitle=A stepped approach to J2EE testing with SDAO
publish-date=03042003
author1-email=brownkyl@us.ibm.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers