This module is a reusable JDBC wrapper which provides a mechanism for Error Injection.
It enables testing of exception handling within a JDBC3 or 4 framework, without the use of mock objects by selectively throwing exceptions either whenever a method is called or after a given number of calls.
The JDBC API, especially version 4, is quite big, so hopefully sharing this module will save other people the wear and tear on their fingers.
The technique used could be applied to any interface, but is particularly suited to the JDBC API as it has a single entry point, the Driver, from which all other objects in the API are obtained.
Test coverage is only worth having if it is complete because:
Each interface within the API has a decorator whose constructor takes an instance. Any of the methods which return another instance of the API will now return a decorated instance.
The decorated instance can be told to throw an Exception, either whenever it is called or after being called a number of times. This enables you to cover cases that would otherwise be impossible to cover without a custom mock.
In the case of the JDBC API you can sub-class your Driver such that it returns a ThrowingConnection instead of a Connection, then use the ThrowingConnection as you would have the Connection.
import java.sql.Driver; import org.hsqldb.jdbcDriver; import org.melati.poem.dbms.test.sql.ThrowingDriver; /** * A decorated Hsqldb jdbcDriver. */ public class HsqldbThrowingJdbcDriver extends ThrowingDriver implements Driver { public HsqldbThrowingJdbcDriver() { super(new jdbcDriver()); } }
You notice that there is uncovered Exception handling associated with failure of ResultSet.close() during database initialisation.
public void testConnect() { ThrowingResultSet.startThrowing(ResultSet.class, "close"); try { getDb(); fail("Should have blown up"); } catch (SQLSeriousPoemException e) { assertEquals("ResultSet bombed", e.innermostException().getMessage()); } ThrowingResultSet.stopThrowing(ResultSet.class, "close"); }
The test is written to test the functionality in the normal way, then subclassed with a ThrowingConnection to test the exception handling.
public class org.melati.poem.test.throwing.DatabaseTest extends org.melati.poem.test.DatabaseTest { public void testFirstObject() { ThrowingResultSet.startThrowing(ResultSet.class, "next"); try { super.testFirstObject(); fail("Should have bombed"); } catch (SQLSeriousPoemException e) { assertEquals("ResultSet bombed", e.innermostException().getMessage()); } ThrowingResultSet.stopThrowing(ResultSet.class, "next"); } }
The exception handling you want to excercise is actually the third call to that method in your test's trajectory to the method under test.
public void testGetObjectInt() { ThrowingConnection.startThrowingAfter(Connection.class,"prepareStatement", 2); try { super.testGetObjectInt(); fail("Should have blown up"); } catch (SimplePrepareFailedPoemException e) { assertEquals("Connection bombed", e.innermostException().getMessage()); } finally { ThrowingConnection.stopThrowing(Connection.class, "prepareStatement"); } }
Do send feedback to TimP at this address.