Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

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

Pattern your way to automated regression testing

Implement popular design patterns to overcome unit-testing hurdles

  • Print
  • Feedback

Page 4 of 4

Thus, early in the real program, we should expect to see the following:

DataSourceManager.put(new JDBCDataSource
("tradeDB", "tradedb", "username", password"));


If we want instead to test our system with XML test scenario data, we would see the following in the JUnit test case setup code:

DataSourceManager.put(new XMLDataSource
("tradeDB", "TradeData.xml"));


Once either statement executes and a class calls DataSourceManager.get("tradeDB") asking for the "tradeDB" DataSource, a factory object that conforms to the DataSource interface (be it XML or JDBC) returns. This DataSource (which can be thought of as a ResultSet factory) is an abstract factory precisely because the client code doesn't care which factory type it is, as long as it adheres to the interface defined in the abstract base class DataSource.

Putting it all together

In the example code, I've implemented a business rule on the TradeProcessor called isMarketCrashing(), which returns a Boolean value. The stock market has crashed if the sell order number divided by the buy order number exceeds a certain ratio. The JUnit test, com.paulitech.examples.test.TradeProcessorTest, tests for this market-crashing condition on two distinct data sets (TradeSourceCrashing.xml and TradeSourceNotCrashing.xml), with two different results. Try altering the XML data by hand to make the unit test fail (for example, insert more buy orders so isMarketCrashing() returns false). Then fix the data and run the tests again. Make sure you understand why the tests fail or succeed.

Final thoughts

Due to space constraints, I didn't explain in detail the full implementation of the abstract classes' XML and JDBC versions. The main point of this article shows you how to apply well-known design patterns in order to unit test code that accesses databases. My team and I had to overcome many interesting hurdles when creating this framework; I encourage you to peruse the source code for a fuller understanding.

Although for a new, built-from-scratch application you could insert the abstraction layer at a higher level than the code that accesses database result sets, many existing applications have core processing logic intermingled with database access code. This logic usually needs unit testing as much or more than any other system part. Attempting to refactor the design at a higher level can exacerbate the trauma to the code and increase the chance of introducing new bugs.

In this example, we could refactor the common iteration logic into an abstract base class, and create two TradeProcessor subclasses, with two different getPendingTrades() method implementations (one for JDBC, one for XML) that return a Collection of Trade objects to the superclass for iteration. (Incidentally, this would utilize the Template Method pattern). But this approach requires tearing apart and reimplementing the core logic, a potentially risky maneuver. By inserting the abstraction layer at the database access level, the core logic is preserved, and you still gain the coveted property of unit-testability.

About the author

Kevin Pauli is an independent software consultant and founder of PauliTech Corporation. He specializes in providing software solutions with Java and XML using industry standard design patterns and open source software. His clients include IBM, NEC, and Sprint. He lives in a suburb of Dallas, Texas with his wife and various animals.

Read more about Core Java in JavaWorld's Core Java section.

  • Print
  • Feedback

Resources