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.