Tuesday, 24 March 2009

Simple transactional unit testing database

Last week I wrote a post about using Google Guice to perform transaction handling using interceptors.
When it came time to write some database level unit tests, I wanted to have the same kind of simple transaction handling so that every unit test is rolled back on completion. Rolling back the changes each test makes is necessary for the tests to be repeatable and consistent.

TestNG provides convenient way to do processing before and after each method is called using @BeforeMethod and @AfterMethod. All you need to do is annotate methods that you want automatically rolled back with @Transactional then check for it and act accordingly. The same could be done with JUnit using a RunListener.

Below is my implementation. I use just one connection per unit test, it can easily be adapted across multiple connections.
public abstract class BaseUtilTest
{
private static final Logger log = Logger.getLogger(BaseUtilTest.class);
protected Connection con;

@BeforeClass
public void init()
{
Injector myInjector = Guice.createInjector(new TestModule());
myInjector.injectMembers(this);
}

@BeforeMethod
public void before(Method method) throws Exception
{
con = ConnectionUtils.getConnection();
if (method.isAnnotationPresent(Transactional.class))
{
//do transactional
}
}

@AfterMethod
public void after(Method method, ITestContext context) throws Exception
{
if (method.isAnnotationPresent(Transactional.class))
{
//rollback any changes
rollback();
}

ITestResult result = Reporter.getCurrentTestResult();
if (!result.isSuccess() && result.getThrowable() != null)
{
log.error(method.toGenericString(), result.getThrowable());
}
}

void rollback()
{
if (con == null)
{
log.warn("No bound connection to rollback");
return;
}
log.info("Rolling back transaction");
try
{
con.rollback();
}
catch (SQLException e)
{
log.warn(e);
}
}
}

No comments:

Post a Comment