| 
   
     | 
        
            | jdbcUnit |  
            | documentation |  
            | download |  
            | support |  
            | contact 
 |  |  
            | 
 
   |  | IntroductionOverviewThe jdbcUnit system records all the activity for the registered JDBC drivers.
While recording, each time the same SQL statement is executed, the recorder returns the 
previously recorded results set.In the current version only the use of SQL statements are recorded. 
So, returning the previous result set or number of affected records each time the 
same query is preformed does not lead to errors.
 Both prepared and direct statements are supported.
Once a set of SQL statement are recorded the same application can switch to 
playback mode. The actual database is not accessed anymore, an exception is 
raised if an unknown query is executed. Quick StartHere we just show a very quick step by step of jdbcUnit usage. For detailed 
explanation please refer to following parts.RecordingAssuming we wrote a book store application, using the JDBC/ODBC driver. Let's record
the new book search, the ones bought this year.
setup jdbcUnit to record activity for the desired database
   
  
   | 
  JDBCUnit.registerDriver("sun.jdbc.odbc.JdbcOdbcDriver"); |  jdbcUnit does not allow to record the access to one database only, as soon 
as one registers a driver all the connections are recorded. We think client code
wants to record all the database activity...
create a store object (a store object is an interface that exposes file-like 
behavior) where jdbcUnit will save the recorded data
Here the activity is to be saved to CSV (comma separated values) format files, the 
"test-data.csv" is the repository (aka toc) that refers to other files
   
  
   | 
  File csv = new File("test-data/test-data.csv");
 Store store = new JavaFile(csv);
 
 CSVMedia media = new CSVMedia(store);
 | create and set the recorder that jdbcUnit is going to use, here we use 
a media recorder. And start the unit so that it is ready to record or play back.
   
  
   | 
  JDBCUnit.start(new MediaRecorder(media)); | start recording, enter the recording mode
when a SQL statement is executed the recorder saves the result set
Sorry for all the deprecation with
   
  
   | 
  PreparedStatement stmt = connection.prepareStatement("SELECT * FROM books WHERE buy_date >= ? ");
 
 java.sql.Date now = new java.sql.Date(System.currentTimeMillis());
 java.sql.Date jan1 = new java.sql.Date(now.getYear(), 1, 1);
 
 stmt.setDate(1, jan1);
 
 ResultSet rs = stmt.executeQuery();
 
 ...
 |  Dateclass...Executing the same code again won't access the database, the recorder is somehow 
playing back.
 To find the previous result sets the recording system use a key made of the database 
url and the SQL statement. The SQL statement is normalized so that syntactic changes 
to the satements don't result in error (e.g. SELECT *    FROM BOOKSis 
equivalent toselect * FROM books. The code shown here to access the database remains unchanged using jdbcUnit
or not. We show it here only to trace the lifecycle of a recording session.
at the end of the application, stop the recording
at this stage a new folder named "test-data" was created in the current 
directory that contains a file named "test-data.csv". This file contains one 
entry with the "SELECT * FROM BOOKS" linked to another file named "test-data.1.csv". 
The later file contains the result from the query.
 The sequence of  JDBCUnit usage is important, see the unit usage 
sequence testsPlaying backNow that we have a recorded database activity we just can play back.
first set the right recorder to jdbcUnit, see previous section
switch to playback mode
when an SQL statement is executed the recorder returns the result set 
saved from the recording sesssion
   
  
   | 
        ...ResultSet rs = stmt.executeQuery();
 ...
 |  ArchitecturejdbcUnit uses a recorder paradigm. 
So, the JDBCUnitclass is the actor that knows how to capture 
database activity and provide the currentRecorder. 
The JDBCUnitactor installs a fictious JDBC driver 
(DriverWrapper). 
The DriverWrappercreatesConnectionWrapperobjects each time a connection is needed. 
The ConnectionWrapperholds some actual connection and providesStatementWrappers when a statement is create or aPreparedStatementWrappers when a prepared statement is created. 
When a statement is executed, the statement wrapper delegates the recording 
to the current Recorder. The picture is, if the jdbcUnit 
is in recording mode: 
If the jdbcUnit is in playback mode:check to see if the track (the result set) is already recorded,  
if the track does not exist execute the query and add the result
otherwise, ask the recorder the previous result
 
ask the recorder the previous result
 
The JDBCUnitactor, for convenience, also knows how to work with a 
givenStore. AStoreis something like a file, that can 
be feed with any content and can have children stores. IfJDBCUnitis 
given aStoreit uses theMediaManagerto get a media 
able to handle the store, then creates a recorder that uses medias... 
aMediaRecorderobject. 
MediaRecorderobjects know how to handle a recording 
support called aMedia. 
A Mediais used by the recorder to write tracks (to SQL statement 
results) and get them back. 
Currently two Mediaimplementations exist:CSVMediaandMediaStack. 
A CSVMediacreates several text files (in CSV format) to store 
all the result sets. One last file is created to hold the mapping between 
statements and the files where the results are stored. 
A MediaStackuses a stack of medias, the top one is one that 
records new result sets. The stack is used only to retrieve recorded data 
(from top to bottom). Availabe tests
Two categories of tests exist: the classic unit tests, that directly test 
a class implementation and a conformance test suite.  Available unit testsConformance testsThe conformance tests suite test if a class, that implements the Mediainterface, does fullfill its contract.All you need to provide is a
 MediaFixtureto validate a newMediaimplementation.Two media implementations exist in jdbcUnit and both do conform (see
CSV Media fixture and
CSV Media Stack fixture).
Conformance tests are:
 ExampleLet's build a very small part of an application that retrieves new books from a 
book store database.Test searching an empty database...Let's write the test.
To make this test pass create the
   
  
   | 
public class BookStoreTest extends TestCase {
 public BookStoreTest(String name) {
 super(name);
 }
 
 public void testNoNewBooks() throws Exception {
 BookStore bs = new BookStore();
 
 Book[] books = bs.getNewBooks();
 
 assertEquals(0, books.length);
 }
 }
 |  BookandBookSoreclasses.
   
  
   | 
  public class BookStore {public Book[] getNewBooks() {
 return new Book[0];
 }
 }
 
 public class Book {
 }
 |  Test one book is foundAdd next test toBookStoreTest.
The
   
  
   | 
public void testOneBook() throws Exception {
 
 initData();
 
 BookStore bs = new BookStore();
 
 bs.setToday(new Date(2004, 6, 6));
 
 Book[] books = bs.getNewBooks();
 
 assertEquals(1, books.length);
 
 }
 |  initDatamethod inserts the result data in a mock media object, 
created in the setup method
You notice we ask jdbcUnit to catch the ODBC activity 
(the
   
  
   | 
void initData() {
 
 List data = new ArrayList();
 
 data.add("Hello");
 data.add("2004/02/27");
 
 result.add(data);
 
 }
 
 protected void setUp() throws Exception {
 
 result = new ArrayList();
 header = new ArrayList();
 
 header.add("name");
 header.add("boughtDate");
 
 result.add(header);
 
 JDBCUnit.registerDriver("sun.jdbc.odbc.JdbcOdbcDriver");
 JDBCUnit.start(new MediaRecorder(new MockMedia(result)));
 JDBCUnit.replay();
 
 }
 
 protected void tearDown() throws Exception {
 JDBCUnit.resetDrivers();
 }
 
 List result;
 List header;
 
 |  registerDriverpart).Also, you can see that the way the
 setUpmethod was written
does not break the first test, no data is added to the pseudo-database.The
 tearDownmethod removes all the registered JDBC drivers.The mock media is implemented in the
 MockMediaclass:
Now, we need to change the code in the
   
  
   | 
public class MockMedia implements Media {
 List values;
 
 public MockMedia(List values) {
 this.values = values;
 }
 
 public void open() {
 }
 
 public void close() {
 }
 
 public void delete() {
 }
 
 public int countTracks() {
 return 0;
 }
 
 public void foreachTrack(MediaVisitor visitor) {
 }
 
 public boolean existsTrack(String dbURL, String sql) {
 return false;
 }
 
 public void newTrack(String dbURL, String sql, List columnNames) {
 }
 
 public void write(List row) {
 }
 
 public void closeTrack() {
 }
 
 public Iterator getTrack(String dbURL, String sql) {
 return values.iterator();
 }
 
 public void deleteTrack(String dbURL, String sql) {
 }
 }
 |  BookStoreclass.First, the
 getNewBooksmethod:
Here we write the actual JDBC code, if we use
   
  
   | 
  public Book[] getNewBooks() throws SQLException {Connection con = DriverManager.getConnection("jdbc:odbc:MyBookStore");
 
 PreparedStatement stmt = con.prepareStatement(
 "select * from bookstore where boughtDate >= ?");
 
 // again, sorry for deprecated method
 stmt.setDate(1, new Date(Integer.parseInt(today.substring(0, 4)), 1, 1));
 
 ResultSet rs = stmt.executeQuery();
 
 int len = 0;
 
 while (rs.next()) len++;
 
 return new Book[len];
 }
 
 String today = new Date(System.currentTimeMillis()).toString();
 |  getNewBookswithout 
starting jdbcUnit the actual database is accessed.Next, add the new
 setTodaymethod:
   
  
   | 
  public void setToday(Date today) {this.today = today.toString();
 }
 |  We definitely don't need to create any database, neither need to create an 
ODBC entry named "MyBookstore".
 
 Tips
As said previously, Storeobjects are used by theCSVMediaclass. As this may lead to a lot of files, that could get big, we can automatically
save everything in a ZIP file:
A test-data.zip file will be created containing all the saved files.
   
  
   | 
    File dir = new File("test-data/test-data.zip");
 Store zip = new ZipStore(dir);
 Store store = zip.add("data.csv");
 CSVMedia media = new CSVMedia(store);
 | 
One can use the generated files to make statistics about the way the application
uses tha database. May be it could help in improving the code.
Checking the number and the size of the generated files may show wrong SQL 
statements. 
We discovered a bad written join, as we noticed that one file was strangly
rather big.
 One file, the repository, contains, when using a 
CSVMediaobject, the mapping from SQL statements to the result files.
Using CSV format files makes it "easy" to maitain the files without having to
repeat recording each time we need to change the data. As the repository is also
very simple to handle, we can add remove or change the SQL statements.
We can cheat with the files used by the recorder, we can redirect several 
select results to the same file.
 |