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"); } }
The java.sql API has changed quite a bit. There were many new methods added in JDBC4. This library produced two different out put jars.
The JDK7 java.sql
breaks backwards compatibility by adding new methods and
and one new class. As Oracle put it in their Incompatibility notes:
RFE: 2164549
Area: API: JDBC
Synopsis: New RowSetFactory
Interface
to allow Creation of a RowSetFactory
Description: New API was introduced to support RowSet 1.1 and,
specifically, the ability to write more portable code by creating
a RowSetFactory
.
As part of this update, the definition of some constants has changed slightly,
but should not affect most users.
Nature of Incompatibility: source
RFE: 6843995
Area: API: JDBC
Synopsis: New JDBC Methods, Including new Methods in Interfaces
Description: For the Java SE 7 release,
there are new methods to support JDBC 4.1.
This includes methods added to the java.sql.Connection
,
java.sql.Driver
,
javax.sql.CommonDatasource
, and java.sql.Statement
interfaces.
Because all methods of an interface must be implemented,
previous code that uses these interfaces will not compile on Java SE 7
unless you add the new methods.
See the JDBC documentation for more information.
Nature of Incompatibility: source
Do send feedback to TimP at this address.