Database TDD Part 4: Backing Into Tests

by Jeff Langr

October 12, 2005

When extracting methods into another class, the tests that cover these methods may be best suited for staying on the original class. This leaves you with no tests directly covering the extracted code.

In the case of the JdbcAccess code, the execute and executeQuery methods remain covered indirectly through tests in UserTest. My preference is to take the time and immediately add tests that directly exercise the newly exposed public interface. Here’s JdbcAccessTest:

    import java.sql.*;
    import java.util.*;
    
    import junit.framework.*;
    
    public class JdbcAccessTest extends TestCase {
       private static final String TABLE = "JdbcAccessTest";
       private JdbcAccess access;
    
       protected void setUp() {
          access = new JdbcAccess();
       }
    
       protected void tearDown() throws SQLException {
          access.execute("drop table " + TABLE);
       }
    
       public void testExecute() throws SQLException {
          access.execute(createTableSQL());
          assertEquals(0, count());
       }
    
       public void testExecuteQuery() throws SQLException {
          access.execute(createTableSQL());
          List row = access.executeQuery(createCountSQL());
          assertEquals(1, row.size());
          assertEquals(0, getInt(row, 0));
       }
    
       private int count() throws SQLException {
          List row = access.executeQuery(createCountSQL());
          return getInt(row, 0);
       }
    
       private String createCountSQL() {
          return "select count(*) from " + TABLE;
       }
    
       private int getInt(List row, int column) {
          return Integer.parseInt(row.get(column));
       }
    
       private String createTableSQL() {
          return "create table " + TABLE + " (x varchar(1))";
       }
    }

In order to get this working, I had to make a small change to the JdbcAccess code:

       private List getRow(ResultSet results) throws SQLException {
          List row = new ArrayList();
          for (int i = 1; i <= results.getMetaData().getColumnCount(); i++)
             row.add(results.getString(i));
          return row;
       }

So getRow is now generalized to any number of columns, which it still presumes are all Strings. This adds some expense in parsing the column when it's not a String, as in the JdbcAccessTest method getInt (used for column count). It's an expense that I'm willing to accept for the time being, but it does suggest that I want to make a note about running performance tests at some time in the future.

The other relevant lesson today is that I followed the refactoring step of eliminating duplication in the test, up to a point. That point is to the readability of the test in question, or more specifically, to the point that the test still clearly shows the thing that's being tested. Eliminating duplication one more step would mean removing the execute call from testExecute, and the executeQuery call from testExecuteQuery. I'd rather not obscure that code. This is an example of where simple design rule #3 (expressiveness of code) can trump #2, elimination of duplication. Usually for me, this occurs only in test code.

Share your comment

Jeff Langr

About the Author

Jeff Langr has been building software for 40 years and writing about it heavily for 20. You can find out more about Jeff, learn from the many helpful articles and books he's written, or read one of his 1000+ combined blog (including Agile in a Flash) and public posts.