AbstractTransactionalSpringContextTests meets TestNG

Introduction

In the search of an alternative to JUnit, in relation to the testing of our classes, we -Mikel and I- decided to give TestNG a try. This testing framework takes advantage of a useful feature introduced in the release 5.0 of the Java Language: the annotations. In addition, it provides a rich set of facilities to ease the unit and integration testing of Java classes, like groups and data providers. Unfortunately, these are very well explained in the home page of the project, so let´s consider them as being out of the scope in regards to the main thread of this post.

The Problem

What we want to have: test cases running within a transactional context AND take advantage of the TestNG facilities. The former is neatly addressed by a class named AbstractTransactionalSpringContextTests, present in the Spring´s mock package. The latter, well, pretty obvious. What it isn´t so obvious is the way to combine both. Why? TestNG does not provide a direct way to surround the test cases with a transaction, so a rollback action is performed at the end of every test. This way, we are able to avoid side effects between test executions, because one test inserts a row and the next one didn´t expect it, or because a database row is updated between executions breaking some assertions, or because … you get the idea. When we decided to start using TestNG as our main framework for testing, this absence lead us to ask Cedric Beust, the TestNG project leader, about some way to accomplish with the transaction stuff. He responded us very kind and rapidly, recommending to implement the rollbacking in an @AfterMethod method.

TransactionRollbackTearDownTestCase

Note: for this class’s name origin, take a look at this site

Probably Cedric is completely right, but we like how AbstractTransactionalSpringContextTests does its business, and of course, the integration with the Spring´s IoC in order to inject the dependencies to the test classes. For that reason, we have come up with an adapter class, which permits us to run TestNG test cases within the transactional context provided by the Spring class. It is kinda adapter because we change the way the class AbstractTransactionalSpringContextTests is used (i.e. inheriting from it), right now we can extend from the new base class and define our test cases as the usual TestNG manner (i.e. through the use of annotations). It is not an adapter as in the software design patterns.

The class is named TransactionalRollbackTearDownTestCase, and following there is a diagram of its relationships:

TransactionalRollbackTeardownTestCase

The code of this class could prove more descriptive than me trying to explain how it does its job:

package com.centuryminds.test;

import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
import org.springframework.test.AbstractTransactionalSpringContextTests;

import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

/**
 * Base test class which permits: 1) Inject dependencies defined in a Spring
 * context. The aforementioned dependencies must be declared as protected
 * members 2) Perform a rollback action after every test. Basically, it
 * acts as an adapter between the TestNG test case structure and the facilities
 * provided by the class {@link AbstractDependencyInjectionSpringContextTests}
 * in the Spring package
 *
 * @see AbstractDependencyInjectionSpringContextTests
 * @see AfterMethod
 * @see BeforeMethod
 */
public abstract class TransactionRollbackTearDownTestCase extends
		AbstractTransactionalSpringContextTests
{

	/**
	 * Test Fixture
	 *
	 * @throws Exception if a error occurred during the fixture execution
	 * @see {@link AbstractDependencyInjectionSpringContextTests#setUp()}
	 * @see {@link AbstractDependencyInjectionSpringContextTests#setPopulateProtectedVariables(boolean)}
	 */
	@BeforeMethod
	protected final void before() throws Exception
	{
		setPopulateProtectedVariables(true);
		super.setUp();
	}

	/**
	 * Test Teardown
	 *
	 * @throws Exception if a error occurred during the teardown execution
	 * @see {@link AbstractDependencyInjectionSpringContextTests#tearDown()}
	 */
	@AfterMethod
	protected final void after() throws Exception
	{
		super.tearDown();
	}
}

As it can be seen, this class is really simple. It defines two final methods, both TestNG-annotated which delegate the test fixture and teardown actions to the underlying parent implementation. Therefore, every time a test case method is going to be executed, before() will inject the dependencies defined in the Spring context and will create and start a new transaction context. The method tearDown() in turn will perform a rollback action just after the test ends.

Extending the Hierarchy

Obviously, the class above is not enough: where are the dependencies to be injected defined? We have to tell AbstractTransactionalSpringContextTests where to look for them. This can be achieved by a subclass of TransactionalRollbackTearDownTestCase, for example:

package com.centuryminds.test;

/**
 * Base test class that specifies the spring contexts containing the dao
 * definitions. The subclasses must provide an implementation of the method
 * {@link #getConfigLocations()}, specifying the files with the spring contexts
 * holding the definitions of the dao and its dependencies
 */
public class BaseDAOTestCase extends TransactionRollbackTearDownTestCase
{
	/**
	 * @see org.springframework.test.AbstractDependencyInjectionSpringContextTests#getConfigLocations()
	 */
	@Override
	protected String[] getConfigLocations()
	{
		return new String[] { "classpath:applicationContext-datasource.xml",
				"classpath:applicationContext-service.xml" };
	}
}

The Actual Test Class

One important benefit of the separation above is that we could define several BaseDAO* classes, each one of them declaring its own context set. Let´s see an example DAO test class extending from BaseDAOTestCase:

package com.centuryminds.test;

import java.util.HashSet;

import org.testng.annotations.Test;

import com.threefish.aquarium.dao.hibernate.GenericHibernateDAO;
import com.threefish.aquarium.model.Role;
import com.threefish.aquarium.model.User;

/**
 * Tests for RoleDAO
 */
public class RoleDAOTest extends BaseDAOTestCase
{
	/** role dao */
	protected GenericHibernateDAO roleDAO;

	/** role */
	private Role role;

	@Test
	public void testCreate() throws Exception
	{
		int amount = roleDAO.findAll().size();

		// let's create a new role to persist
		role = new Role();
		role.setName("testrole");
		role.setDescription("Master of Universe");
		role.setUsers(new HashSet());

		roleDAO.makePersistent(role);

		assertNotNull(role.getId());
		assertEquals(amount + 1, roleDAO.findAll().size());
	}

	@Test
	public void testSuccessfulGet() throws Exception
	{
		role = roleDAO.findById(Long.valueOf(1));

		assertEquals("admin", role.getName());
	}

	@Test
	public void testUpdate() throws Exception
	{
		role = roleDAO.findById(Long.valueOf(1));
		role.setDescription("Super Administrator Role");
		roleDAO.makePersistent(role);

		assertEquals("Super Administrator Role", role.getDescription());
	}
}

The RoleDAO test class makes use of a custom generic DAO framework, but despite that I think it is pretty self-explanatory. We shall try to explain the generic DAO related interfaces and classes in another post when we get a chance, but for now we have enough.

The test methods are executed within a transaction per test method, which is rollbacked at the very end of it. So, the role insert in the first test (testCreate()), would be rollbacked and future tests depending on the initial state (i.e. the state before testCreate() ran) wouldn´t be affected. We accomplished with the goal of executing the test cases in isolation. Thanks Spring! We accomplished with the goal of using some of the features offered by TestNG. Thanks to you as well!

Future

We are aware about the drawbacks of this solution, and we are pretty sure about the fact that there must exist a more sophisticated solution out there, hence we are very willing to listen to your suggestions to improve the general structure of our current one. We’ll stay tuned …

About these ads

7 Responses to AbstractTransactionalSpringContextTests meets TestNG

  1. nico says:

    First drawback i see is that this way of joining spring and testNG, keeps in your system the dependency with JUnit framework… which as you ‘re trying to use testNG is quite undesireable.

    Possible solution is to replicate spring mock test classes eliminating its dependency on junit, although the best solution would be that spring fellows put some classes right out-of-the-box which ‘ve no dependency on junit.., and then subclass them to add the junit dependency.

  2. Nico, thanks for your comments. In relation to the issue with the JUnit dependency, I think you´re right. As you say, it would be great to count on such features in Spring. In fact, Hani/Cédric told us there are some classes in the new Spring release (2.0) to cope with that, but after taking a peek at it, as of that time those classes didn´t use the annotations facility, but the commons´ javadoc tags.
    Anyway, the actual point to take into account is the one you state: to get rid of the JUnit dependency whenever you want to use TestNG. Let´s see if we come along with this in the sort term :)

  3. GojiraDeMonstah says:

    The JUnit dependency is unfortunate, but I think the solution as a whole is fantastic. I just googled on combining TestNG and Spring on a whim and found this article – it’s *exactly* what I needed. The nice goodies the testNG Eclipse plugin gives you with the heavy duty IOC and transaction management from Spring – it is the best of both worlds.

    Nice work Rubén!

  4. Hani Suleiman says:

    Hi there,

    All of the spring test heirarchy exists in testng, in the org.testng.spring package! Unfortunately there are no builds for this, so if you grab the source andgo to the spring directory, run ant, you’ll end up with a jar file that has classes matching all of the names of the ones that Spring ships, without any JUnit dependencies.

    You can even do stuff like annotate test methods with @Transactional if you want.

  5. Glad to hear that, Hani! I’ll check them out and see how they can fit to our needs. Thanks very much

  6. nico says:

    since
    Spring 2.5 RC1 (2007-10-22)

    Brand new dedicated testng classes come out-of-the-box with spring, to provide transactional tests and dependency injection tests…

    this releases us from the problems discussed here…

    finally no junit dependency is needed.

    cheers

  7. What’s up it’s me, I am also visiting this web site on a regular basis, this web page is actually good and the visitors are in fact sharing good thoughts.

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: