Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
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
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.
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.
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.
Read more about Core Java in JavaWorld's Core Java section.