Be polite, share your transaction

November 16 2008

it’s about six months since i’m using the Spring framework in our current codebase (i played with the .NET version in the past), and even if my team don’t usually like to use “imposed” frameworks, or don’t like to use a framework for doing everything, thanks to Spring i’ve been able to do something extremely impressive: share a transaction between an HTTP integration test and the webapp under test. here it comes.

there are a couple of patterns when testing data access code, in order to get rid of all the test data setup. in the worst case, you can manually delete all inserted data, if you can distinguish them from the existing data (basically, ids are created by the test, or there’s a criteria such as “latest” data – some versioning field is required). better scenario, you open a transaction in the test setup and perform a rollback in the teardown. that’s nice, you only need to share the transaction beetween the test and the system under test. with or without Spring, it’s a common strategy, well documented in literature and for specific techologies.

well, everything goes fine, untill you reach the system level, and you still want to perform a leave-data-as-you-find integration test. that’s what i was trying to do a few weeks ago, in our current project. we decided on having the simplest end-to-end test, from presentation deep through data tier (compiled SQL stored procedures on the database). we already had a great test stack for that, a combinantion of

  • Winstone, for deploying inside an in-memory servlet container the whole webapp
  • HttpUnit and HtmlUnit, for connecting to the webapp
  • Spring datasource injection, using the classpath as a link seam to switch configuration to a development database

in other words, we could be able of running the webapp connecting to our in-house database, and test the resulting HTML content, without the need of any deploy outside the test execution: all in memory, baby! that’s fine, at least untill you need to test some “actions”, modifying the underlying data. that is the time when you need something more.

the simplest solution was to “detect” some id for the inserted data, and hopefully we had! we randomly generated a description and used it inside a DELETE SQL statement in the teardown. but soon things got worse, and this strategy was no more feasible. so i thought “well, i can share a connection beetween the test and the webapp, can’t i?”. well, it’s not so easy!.

the main point is that while running a test with a in-memory servlet container, the webapp has its own WebApplicationContext, and there’s no way to get it from the test: even if you refer to the same xml file, the application contexts are two separate instances, connecting to the same database, but through different connections.

remember what i said before, today story is about how to share a transaction between test-code and webapp: hold on, we’re getting there! main actors are two nice features of Spring:

  • shared application context, have both webapp’s WebApplicationContext and test ApplicationContext refer to a parent context
  • TransactionAwareDataSourceProxy, embed inside a transaction an existing datasource

shared application context is usually used inside EAR modules. by the way, this is all i had to change in the configuration:

  • in web.xml add a value for param parentContextKey, such as shared-resources-context
  • create a beanRefContext.xml file, with a ClassPathXmlApplicationContext on classpath:datasources*.xml
  • remove datasource definition from applicationContext.xml

we can now refer to the shared (parent) application context this way:


@Before
public void setUp() {
    BeanFactoryLocator singletonFactoryLocator = ContextSingletonBeanFactoryLocator.getInstance("beanRefContext.xml");
    beanFactory = singletonFactoryLocator.useBeanFactory("shared-resources-context").getFactory();
    dataSource = (DataSource) beanFactory.getBean("dataSource");
}

almost done. next thing is to assure webapp and test access the same datasource, and that’s the main business of TransactionAwareDataSourceProxy, used in combination with a SingleConnectionDataSource (both from Spring).

for the lifecycle of a test, everyone is accessing to the same connection, which now is transaction-aware. finally, we can use a transaction manager to start and rollback a transaction at test boundaries.

quite a long story, hope you enjoyed. i gzipped a simple webapp here, please have a look yourself: it’s an Eclipse project with code, test, sql script and all required libs, but it still lacks a README file (i’m quite tired and i want to post, i’ll add notes later).

i really hope this can be useful to your project, feel free to use it, and send me feedback.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: