Database, Hibernate, and Spring Testing with TestNG
Published: 11:03 PM GMT+12, Wednesday, 15 March 2006 under:
technology
springframework testing java agile testng hibernate sql
springframework testing java agile testng hibernate sql
In a moment of bravery I'll admit to my personal "Daiy WTF" of the week, and how what initially was the making of a really crap day ultimately better for the health of the ${project}.
Ever since the deployment of the spanking new Hibernate 3.1.2/Spring based version of ${project} I've been seeing a regular, but intermittent, "unreproducable in dev" problem trigger in our production environment, so Monday saw me adding additional debug logging around the particular piece of code, writing a few localised tests which identified some other potential issues but left me nowhere closer to the problem I was initially trying to reproduce/test/fix.
As close-of-business approached ${project} was deployed to our staging environment for a quick testing run through before deploying to the production environment. Nothing had really changed I said to myself - only a few debug and profiler statements and a class extracted to an interface - nothing will break - all the tests hitting those classes pass...
...now - if it had only been debug and profile statements that would have been correct, unfortunately during the interface extraction - IntelliJ IDEA did the (normally) smart thing of renaming all references to the class to its new name. Only the spring configuration was wanting the newly created interface...
Long story short - it blewup on me. Rollback, patch, build, really check it this time, deploy - all is happy and I know have extra debug information being recorded to further my bug hunt.
Anyway, the result of this was it gave me a swift kick in the pants about testing from the container, so Tuesday saw be finally saw me setting up my TestNG tests to create a clean room database, load up spring and hibernate, THEN run my tests against a fresh live system, and then happily clean up after itself.
What shocked me most is how easy it all turned out to be thanks to TestNG's flexable group and configations.
Finally we're getting to the meat of this post I hear you say...
So anyway, I now have a DatabaseInitialization class with the following methods:
/**and
* @testng.configuration groups="hibernate" beforeSuite = "true"
*/
public void createDatabase() throws IOException, SQLException,
ClassNotFoundException, JDOMException {
/**
* @testng.configuration groups="hibernate" afterSuite= "true" alwaysRun="true"
*/
public void removeDatabase() throws IOException, SQLException,
ClassNotFoundException {
These two simple methods sit nicely in a standalone class. createDatabase() first connects to the "template1" database (postgresql) and issues a "CREATE DATABASE integration_test" then reads an XML file containing schema DDL and an XML file containing SQL for default data.
The removeDatabase() method also connects to the template1 database and simply issues a "DROP DATABASE integration_test" to clean up.
Note: alwaysRun=true is used because by default, TestNG will not run an afterSuite method if the beforeSuite methods raise an exception.
Next in line is an abstract SpringTest class:
public abstract class SpringTest {
protected XmlBeanFactory springContext;
/**
* @testng.configuration groups="hibernate" beforeTestClass = "true"
*/
public void setup() throws IOException {
String baseDir = System.getProperty("base.dir");
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Properties props = new Properties();
props.load(getClass().getResourceAsStream("/test-settings.properties"));
springContext = new XmlBeanFactory
(new InputStreamResource(new FileInputStream(
new File(baseDir, "web/WEB-INF/action-servlet.xml"))));
ppc.setProperties(props);
ppc.postProcessBeanFactory(springContext);
}
/**
* @testng.configuration groups="hibernate" afterTestClass= "true"
*/
public void tearDown() {
springContext.destroySingletons();
}
}
The SpringTest class defines two simple methods which run before/after the individual TestClass and simple loads up a spring configuration (which in turn defines the connection pool and hibernate settings) and then tears it down.
Finally we get to write an actual test - and because the setup has been handled for us in SpringTest, our test can jump right in to doing what it does best:
public class MailLogTest extends SpringTest {
/**
* @testng.test groups="hibernate"
*/
public void testMessages() throws MessagingException, IOException,
InvalidMessageException {
MessageStorerSupport mss = (MessageStorerSupport)
springContext.getBean("messageStorerSupport");
UserSearch userSearch = (UserSearch)
springContext.getBean("userSearch");
assert mss != null : "Message storer object not found";
assert userSearch != null : "UserSearch object not found";
UserValue user = userSearch.findUserValueByUserName("user");
assert user != null : "User object not found";
The test just jumps straight in and pulls a bean out of the spring context and starts doing things against a successfully bootstrapped spring/hibernate container against a clean database. Now, not only do I now have tests which work consistently against a clean database, I have an implicit test against the spring and hibernate configurations.
Currently these tests are using beforeSuite/afterSuite and beforeTestClass/afterTestClass, but with the next release of TestNG these will be made a bit more modular with DatabaseInitialization being in the "database" group using beforeGroup/afterGroup, and SpringTest being in the "hibernate" group - also with a beforeGroup/afterGroup, but with a dependency on the "database" group.
Of course - if I was extremely anal could have the database recreated and spring/hibernate bootstrapped before each and every test method with just a minor configuration change.
One final change to make the tests even cleaner would be to pull the beans out of the context into protected fields at the SpringTest level.
Small tests.
Large returns.
Building with confidence.
Comments (2)
Nice explanation, thx
left by David . Thursday, 19 October 2006 9:36 PM
It would be nice if you could also provide and the configuration you have done with the testng.xml.Also i am trying to integrate testng with EJB3 testing out of container.Did your tests involved such a setup?