Jeff's Blog

Musings about software development, Java, OO, agile, life, whatever.


Wednesday, October 12, 2005 
Database TDD Part 4: Backing Into Tests

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<String> row = access.executeQuery(createCountSQL());
      assertEquals(1, row.size());
      assertEquals(0, getInt(row, 0));
   }

   private int count() throws SQLException {
      List<String> row = access.executeQuery(createCountSQL());
      return getInt(row, 0);
   }

   private String createCountSQL() {
      return "select count(*) from " + TABLE;
   }

   private int getInt(List<String> 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<String> getRow(ResultSet results) throws SQLException {
      List<String> 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.


Comments: Post a Comment

Links to this post:

Create a Link



<< Home

RSS Feed (XML)

Archives

February 2004   March 2004   May 2004   September 2004   October 2004   January 2005   February 2005   September 2005   October 2005   November 2005   December 2005   January 2006   February 2006   March 2006   June 2006   August 2006   January 2007   February 2007   March 2007   April 2007   September 2007   October 2007   November 2007   December 2007   January 2008  

This page is powered by Blogger. Isn't yours?