February 27, 2004
DAO testing

A good indication of when you are really starting to get to grips with a subject, I find, is when you start to know the right questions to ask. (Knowing the answers to these questions comes much later. That's expertise.) Once you are at this point, you can really start to take off: if only because once at this point, Google can usually help you out.

Well, when it comes to JNDI, I'm nowhere near this point. I'm out of my depth here, and I know it.

I'm putting together the unit tests for my DAOs. (Yes, yes, I know I should have written the tests before writing the DAOs themselves. Let's face it; I'm a bloody amateur.) The DAOs get their connections from a pool, like so. This clearly isn't going to work in my unit tests unless I sort out the context stuff beforehand - and I haven't a clue how to do this. Putting together a harness DataSource is trivial. It's the InitialContext and Context stuff that I don't understand.

I came across Using mock naming contexts for testing by Simon Brown, which looks like it ought to get me going - but my brain just isn't making the leap. Sigh.

(Simon's a lovely chap, BTW. I've met him a couple of times at the London Java Meetups.)

Posted to Java by Simon Brunning at February 27, 2004 01:47 PM
Comments

I must admit I don't know a wolhe lot about JNDI - I just think of it as a registry for storing stuff. Of late my code tends not to use JNDI directly (having Spring pass in the actual object references with the lookup being performed during component wiring). Can you give a more concrete example as to what you're trying to do?

Posted by: Sam Newman on February 27, 2004 02:19 PM

Hokay. Here goes.

My persistance package contains at its heart a bunch of concrete DAO classes. Client code will never see them directly, but it is they that will do the real work. This is all based on "Core J2EE Patterns - Data Access Object"[1]

These classes consist of a number of methods which mediate between the database and the rest of the system, passing Transfer Objects (simple bean objects) in and out. An example (with eror handling and comments stripped for clarity[2]):

public UserDetails getUserDetails(final String userID) {

    final UserDetails userDetails = new UserDetails();
    userDetails.setUserID(userID);

    final Connection connection = MSSqlDAOFactory.getConnection();

    final String sql =
        "select "
            + StringUtils.join(VALUE_COLUMNS, ", ")
            + " "
            + "from "
            + TABLE
            + " "
            + "where "
            + StringUtils.join(KEY_COLUMNS," = ? " + "and ")
            + " = ? ";
    log.debug("Built SQL statement:" + sql);
    final PreparedStatement statement = connection.prepareStatement(sql);
    mapUserDetailsToStatement(userDetails, statement, KEY_COLUMNS);

    final ResultSet result = statement.executeQuery();
    if (result.next()) {
        mapRowFieldsToUserDetails(userDetails, result, VALUE_COLUMNS);
    }
    
    connection.close()

    return userDetails;
}

private static void mapUserDetailsToStatement(
    final UserDetails userDetails,
    final PreparedStatement statement,
    final String[] columns) {
    for (int columnIndex = 0; columnIndex < columns.length; columnIndex++) {
        final String columnName = columns[columnIndex];
        final int columnNumber = columnIndex + offset + 1;
        final String propertyName = StringUtils.uncapitalize(columnName);
        final Object fieldValue = PropertyUtils.getSimpleProperty(userDetails, propertyName);
        if (fieldValue instanceof String) {
            statement.setString(columnNumber, (String) fieldValue);
        } else if (fieldValue instanceof Timestamp) {
            statement.setTimestamp(columnNumber, (Timestamp) fieldValue);
        } else if (fieldValue instanceof BigDecimal) {
            statement.setBigDecimal(columnNumber, (BigDecimal) fieldValue);
        }
    }
}

The crucial thing here is the MSSqlDAOFactory.getConnection() call[3]. This, when running in the context of a conrrectly configured J2EE container, retrieves a connection from a pool. When running from jUnit, though, there is no pool. So somehow, I need to fake a JNDI environment such that the getConnection() call[3] will return a connection.

As I said, I can easily build a DataSource which will return a Connection - for test purposes, I don't need a pool. But plumbing this into JNDI in such a way that this datasource is returned form the couple of lines below is way beyond me. I just don't know where to start.

Context context =(Context) new InitialContext().lookup("java:comp/env");
DataSource dataSource = (DataSource) context.lookup("jdbc/FooBar");

[1] http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
[2] Hmmm. If I'm stripping my comments for *clarity*, then they can't be such good comments, can they?
[3] http://www.brunningonline.net/simon/blog/archives/001228.html

Posted by: Simon Brunning on February 27, 2004 02:48 PM

The problem here is that the context lookup strings are in the code itself, as is the DataSource creation. This mght be overkill, but you could abstract out the creation of the DataSource (and its JNDI lookup) altogether. Using Spring to create and wire this would work - Spring can create your DataSource object and pass it to your objects. When testing, you use a different Spring configuration - instead returning your fake DataSource object.
Your MSSqlDAOFactory couldbe created and returned from Spring itself - Spring creates the DataSource, creates the MSSqlDAOFactory, and passes the relevent dataSource to the factory. Then, you get the factory from Spring rather than a static method (or you could hide the call to Spring behind the MSSqlDAOFactory.getConnection() call itself).
The petclinic example in Spring shows how a DataSource can be created and passed around...

Posted by: Sam Newman on February 27, 2004 03:15 PM

Forgot a link for Spring:
http://www.springframework.org/

You could also have the DataSource creation handled in a configuration file of your own choosing, and just have a different one for testing. Spring will do this and a whole lot more of course...

Posted by: Sam Newman on February 27, 2004 03:16 PM

There is probably no need your DAOs need to know about a connection pool. If your DAOs, which tend to be shortlived, need a connection or a transaction then pass one to them in the constructor. Also check out DBUnit.

Posted by: Bo on February 27, 2004 03:48 PM

Thanks, Sam. I'm not sure that I'm ready for Spring just yet, though I think I'll leek into it for my next project, But abstracting out the creation of the Connections is a fab idea.

Posted by: Simon Brunning on February 27, 2004 04:26 PM

Why deal with JNDI at all? The way I like to do it is to put the connection lookup logic in a protected factory method in my dao. Then in my test case, I extend the DAO and override the getConnection() method to return whatever Connection I want.

public class MyDAO {
    public void saveSomething(Object o){
        Connection c = this.getConnection();
        //use c to save o.
    }

    protected Connection getConnection(){
        //JNDI stuff to get a Connection
    }
}

public class MyTest extends TestCase{
    public void testSaveSomething() throws Exception {
        MyDAO dao = new MyDAO(){
            protected Connection getConnection() {
                return new SomeTestConnection();
            }
        };
        //run your dao test.
    }
}

If you find yourself writing the anon class more than once, switch to a private static inner class.

Bo's solution is equally simple (you could use a mutator method as well), but it ties you to JDBC which may or may not be acceptable.

Posted by: Matt on February 27, 2004 06:13 PM

Use a factory pattern to create the dao, and pass the connection as a parameter to the factory method that creates MyDAO. Or you could use IoC pattern or Dependency injection (as people are calling it now) to create the DAO.

public class MyDAO
{

public void saveSomething(Object o, Connection)
{

//use c to save o.
}

}

public class MyTest extends TestCase
{
private Connection c;
public void setUp()
{
// create a connection in the setup
}
public void testSaveSomething() throws Exception {
MyDAO dao = new MyDao();
dao.saveSomething(o, c);
};
//run your dao test.
}
}

Also, note that most DAOs are stateless, and in a lot of cases all of the DB accessor methods can be made static. So, the class could be rewritten as

public class MyDAO
{

public static void saveSomething(Object o, Connection) throws Exception
{

//use c to save o.
}

}

public class MyTest extends TestCase
{
private Connection c;
public void setUp()
{
// create a connection in the setup and close it in the teardown.
}
public void testSaveSomething() throws Exception {
MyDAO.saveSomething(o, c);
};
//run your dao test.
}
}

Posted by: Alok on February 27, 2004 06:52 PM

Use Inversion of Control (IoC).

That means, your DAO's should get the DataSource passed to them. They shouldn't look it up from JNDI or anything. Instead, have something like your DAO Manager pass the DataSource object to your DAO implementations during an initialization lifecycle phase.

If you do it this way, you can easily change how the DAO's get the Datasource without changing the DAO implementations. Moreover, you have more control in your test cases to decide how to get or wrap your Datasources.

[shameless plug]
Oh, and you check out JingDAO: http://jingdao.sf.net, a DAO framework based on Pico and Avalon.
[/shameless plug]

Posted by: jaaron on February 27, 2004 07:02 PM

Simon, I've posted a response as well as some tips for simplifying JDBC code and avoiding common pitfalls on my blog:

http://weblogs.java.net/pub/wlg/1065

Posted by: Bob Lee on February 27, 2004 09:57 PM

Thanks to everyone who helped out here. I have everything working now. My DAOs now take on (optional) argument for a class implementing a 'DataSourceFactory' interface. By default, the DAOs pick up a factory which gets its data sources from the J2EE container's pool - but for test purposes, I can use somethign else. Works a treat.

Bob and Sean's articles are great, BTW. It's too late to use thier ideas in this project, but next time...

Posted by: Simon Brunning on March 1, 2004 10:06 AM

You can probe ComtorDao. It uses reflexion to map objects on tables.
This library implements insert, delete, update and find actions. You do not require xml configuration files, you don't need to write any insert , delete or update handly

Posted by: Jaime Uriel on April 26, 2008 12:17 AM
Post a comment
Name:


Email Address:


URL:



Comments:


Remember info?