B. Testing with (and without) Spring – Spring in Action, Second Edition

Appendix B. Testing with (and without) Spring

Software is precise in its execution. It does exactly what it is written to do—nothing more and nothing less. The software we write would function flawlessly if it weren’t for one minor problem: we write it. Even though software perfectly executes the instructions it is given, flawed humans are the ones who write the instructions. Human flaws tend to leak into software instructions, resulting in quirks, snafus, and errors.

When you consider the serious consequences of software bugs, it becomes imperative that we test software as much as possible. Even though it’s still possible for bugs to exist in tested code, the odds of a serious bug sneaking into production are greatly diminished by writing comprehensive test suites to exercise every nook and cranny of an application.

In this appendix, we’re going to have a look at where Spring fits within the testing picture. You’ll see how Spring indirectly supports testing by promoting loose coupling. We’ll also explore a handful of Spring classes that you can use to test Spring MVC applications and transactional code.

Introduction to testing

On the surface, software testing seems to be about ensuring the correctness and quality of software. But as we’ve already implied, it’s ensuring that we as developers are giving the correct instructions to the software. If we’re to blame for the mistakes that creep into software, isn’t it also our responsibility to write tests that mitigate the chances that someone will see those mistakes?

Not so long ago (for some of you this may sound like an average workday), testing was an activity that was magically crammed into the last few days prior to pushing software into production. And testing was usually done by people who were labeled as “Quality Assurance.” A huge problem with this approach is that people who had no direct control over the instructions given to the software were allowed virtually no time to verify that it was correct. To say that QA is able to assure quality is hoping against hope.

Although developers have long been encouraged to test their own code, many developers dismissed testing as not being their job. Others performed only minimal smoke tests—if the compiler didn’t complain about errors, there must not be any!

Agile methodologies such as extreme programming and Scrum have brought developer-written tests to the forefront. And frameworks such as JUnit have put the power to write repeatable and automated tests into the hands of developers. Test-infected developers are now writing better and more correct code than ever before.

That’s not to say that QA is obsolete. On the contrary, QA people are trained to think of clever ways to break software, whereas the developer mind-set is focused on clever ways to make software work. I’m only saying that developers should step up to the plate and accept the duty of ensuring the correctness of their code—and for making the lives of QA staff a little easier.

With that said, let’s look at different ways that developers can test their code and see how Spring fits into developer-written tests.

Understanding different types of testing

Testing comes in various forms. In fact, depending on which testing expert you ask, you may find that there are several dozens of types of tests. Table B.1 lists just a few of the most common types of tests.

Table B.1. Software testing comes in several flavors.

Test type

Purpose

Unit

Tests a logical unit of work (class, method, etc.) in isolation

Integration

Tests two or more units of an application together

Functional

Tests a specific function of an application, end-to-end, involving two or more units of an application

System

Tests an application’s functionality through the same interfaces used by the end users of the application

System-Integration

Tests the interactions between two or more collaborating applications

Performance

Tests the performance of an application or system in terms of throughput, load, memory footprint, etc.

There are many more types of tests in addition to those listed in table B.1. But for the purposes of discussing how Spring fits into the testing landscape, we’re going to focus on the first two types of tests listed: unit and integration tests.

Both of these types of testing can be automated using JUnit (http://www.junit.org), a popular open source testing framework. Before we go into Spring’s role in testing, let’s set the stage by going through a quick introduction to JUnit.

Using JUnit

JUnit is a testing framework created by Kent Beck (of extreme programming fame) and Erich Gamma (coauthor of the “Gang of Four” Design Patterns: Elements of Reusable Object-Oriented Software book [Addison-Wesley Professional, 1995]). It is probably the best-known testing framework of all time. Although JUnit was developed with unit testing in mind, several extensions to JUnit have emerged to enable other types of testing.

Creating tests in JUnit is simply a matter of extending the junit.framework.TestCase class and implementing one or more test methods. A test method is any method whose name starts with test. Within each test method, one or more assertions are made. If any of the assertions fail, the test fails.

The following is a simple example of a JUnit test case that tests certain mathematic functions:

import junit.framework.TestCase;

public class MathTest extends TestCase {
  public MathTest() {}

  public void testAddition() {
    assertEquals(4, 2+2);
    assertEquals(99, 33+66);
  }

  public void testMultiplication() {
    assertEquals(0, 5*0);
    assertEquals(-6, 2 * -3);
    assertEquals(625, 25 * 25);
  }
}

Although extremely mindless, this is a valid JUnit test case. When run within a JUnit runner, this test can prove (or disprove) the correctness of the mathematic operators. For example, figure B.1 is a screenshot showing the results of running MathTest within the JUnit view in Eclipse.

Figure B.1. A green bar indicates that all tests have run successfully in JUnit. The code’s clean!

It may be hard to see when printed in grayscale, but there’s a green bar in the user interface. That green bar indicates that all tests have passed. The mantra of JUnit testers is, “If the bar’s green, the code’s clean.”

On the other hand, if the bar’s red then a test failed. To illustrate, here’s a test method that is guaranteed to fail:

public void testThatFails() {
  assertEquals("One", new Integer(1));
}

This test fails because the String value of "One" is not equal to the Integer value of 1. Figure B.2 shows the results when this test method is run in Eclipse’s JUnit view.

Figure B.2. Oops! A red bar indicates that one or more test methods have failed. In this case "One" is apparently not equal to 1.

Again, it may not be apparent from the grayscale image in figure B.2, but a failed test results in a red bar. The red bar tells you that something is wrong in the code and that you have a problem to fix. The failure’s stack trace hints as to what may have gone wrong.

Setting up tests

It’s often important that some test data and collaborators be prepared in advance of the actual test. While it’s quite possible to perform such setup at the beginning of each test method, you may find yourself repeating the setup code across multiple test methods in the same test case.

To consolidate test setup into one location, you should override TestCase’s setUp() method:

public void setUp() throws Exception {
  // perform setup code
}

It’s vital to understand that setUp() is called just before each test method is invoked. If your test case has 10 test methods then setUp() will be called 10 times in the course of the test case. Depending on what your test setup involves, this could have an impact on how long it takes to run your test case.

Tearing down tests

An important tenet of testing is that each test be run independently of the others. Each test must leave its environment in a consistent state after the test is run. This means that if a test method were to alter some data (a file, database, etc.) that is shared among multiple tests, that data should be returned to its initial state upon completion of a test.

Again, it’s possible to have teardown code at the end of each test method. But, as with setup code, doing so will often result in duplication of the teardown code across multiple test methods. Just as TestCase offers setUp() for performing test setup, TestCase also provides tearDown() for performing test cleanup.

To implement test teardown in your test case, override the tearDown() method:

public void tearDown() throws Exception {
  // perform teardown code
}

where setUp() is called just before each test method and tearDown() is invoked just after each test method completes. This guarantees that any cleanup work is done between test methods so that each test method starts in a clean, untainted state.

Now you’ve been given a crash course in the basics of writing JUnit test cases and should be ready to start unit-testing your Spring applications. Before we move on, I’d like to mention that there’s much more to JUnit than has been presented in this section. For more information on JUnit, I highly recommend Manning’s JUnit in Action (2003) and JUnit Recipes (2004).

Spring’s role in testing

I’m often asked how Spring is used when testing. The easy answer is that Spring usually isn’t directly involved in testing. Applications that are developed to take advantage of Spring’s dependency injection are made up of loosely coupled objects, which are, by nature, easier to test. But Spring generally isn’t used within tests.

But I did say that was the easy answer. The truth is that there are a few places where Spring can participate directly in tests:

  • Unit-testing Spring MVC—Spring comes with an extension to JUnit’s TestCase that exposes a handful of convenient assertion methods for verifying the values returned in a ModelAndView object. In addition, Spring provides several out-of-the-box mock implementations such as MockHttpServletRequest and MockHttpServletResponse that are useful for testing controllers.

  • Integration-testing Spring applications—Spring also comes with a TestCase extension that automatically loads a Spring application context for integration tests. It ensures that the application context is loaded once and only once for all test methods.

  • Transactional testing—Another TestCase extension starts a transaction before each test method and rolls it back after the test is complete. This is useful when you’re testing code that is writing to a database because it ensures that the database is left in a consistent state between test methods.

The rest of this appendix examines the ways that Spring can help out with testing. Let’s start by writing some unit tests for the RoadRantz controllers.

Unit-testing Spring MVC controllers

The web layer of an application is often considered one of the most difficult pieces of an application to test. It’s true that determining what goes into a request and what is rendered in a browser presents some interesting challenges in unit testing. JUnit extensions such as Cactus and HttpUnit make this challenge a little less daunting. Nevertheless, the web layer is often left as one of the most untested parts of many applications.

But it doesn’t have to be that way. Take a moment to think about what a Spring MVC controller class does. Forget that controllers have anything to do with the Web. In simple terms, a controller takes input (in the form of an HttpServletRequest) and produces output (in the form of a ModelAndView). It doesn’t render an HTML page—that’s the job of the view layer. In this light, controllers are no different than any other Java class—and thus shouldn’t be any more difficult to test.

To illustrate, let’s write a basic unit test for RantsForVehicleController. The functionality desired from this controller is that it should receive a vehicle’s state and plate number in the request and return a ModelAndView populated with a list of rants. Also, if the request URI ends with .htm then the view name returned should be rantsForDay.

RantsForVehicleControllerTest (listing B.1) shows a JUnit test case that might be used to test RantsForVehicleController.

Example B.1. Testing RantsForVehicleController

In short, the test case in listing B.1 passes in an HttpServletRequest and an HttpServletResponse to the controller and expects to receive a ModelAndView containing certain data. Even though RantsForVehicleController is a command controller, we test via the Controller interface’s handleRequest() method so that we’re testing at the basic level of functionality—the same level at which DispatcherServlet will invoke the controller.

But the test case in listing B.1 isn’t complete. Some unanswered questions remain about the origin of HttpServletRequest and HttpServletResponse that are passed to handleRequest() (as well as what information is contained in the request). Also, doesn’t RantsForVehicleController depend on a RantService to retrieve the rants? Where is that dependency set?

The answer to all of these questions: mock objects.

Mocking objects

When DispatcherServlet invokes the handleRequest() method of RantsForVehicleController, it passes in an instance of HttpServletRequest that is populated with everything that handleRequest() needs to perform its work (or at least as much information as was given it by the servlet container). But when you’re unit-testing a controller, the request doesn’t come from DispatcherServlet because ultimately the request doesn’t originate in a web browser. The request must originate from the test case itself. You need a way to create a request to pass on the call to handleRequest().

We could take the time to write our own implementation of the HttpServletRequest interface. But HttpServletRequest requires so many methods to be implemented, many of which play no purpose in our test. It would be a lot of work to create a suitable HttpServletRequest for our test case.

Fortunately, Spring provides a mock implementation of HttpServletRequest for us to use: org.springframework.mock.web.MockHttpServletRequest. Let’s revisit RantsForVehicleController and replace the declaration of the HttpServletRequest object in testSimpleCase() with the following code:

MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("POST");
request.addParameter("state", TEST_STATE);
request.addParameter("plateNumber", TEST_PLATE_NUMBER);
request.setRequestURI(
    "http://localhost:8080/roadrantz/rantsForVehicle.htm");

Now we have an HttpServletRequest instance to pass to handleRequest(). What’s more, we are able to use some of the convenience methods of MockHttpServletRequest to populate the request with the information needed by Rants-ForVehicleController.

That addresses the request parameter. But what about the response parameter? No worries—Spring also gives us MockHttpServletResponse to accommodate the other parameter of handleRequest(). Replace the HttpServletResponse declaration with the following line:

HttpServletResponse response = new MockHttpServletResponse();

Our test requires no special setup of the response object, so this one line is sufficient to set up the call to handleRequest().

Mocking interfaces with EasyMock

At this point we’re able to call handleRequest() in our test method. But when we do, a NullPointerException will be thrown from handleRequest() because internally a RantService is used to retrieve the list of rants. When the application is running in the Spring container, Spring will inject the rantService property of RantsForVehicleController with a RantService instance. But our unit test doesn’t run in the Spring container. How can we ensure that a RantService has been injected into the rantService property?

What we need is a mock implementation of the RantService interface. Unlike the case of HttpServletRequest and HttpServletResponse, where Spring provided mock implementations for us, Spring didn’t anticipate our need for a Mock-RantService class. So, we must build a mock implementation ourselves.

To make mocking easier, we’ll use EasyMock (http://www.easymock.org).[1] EasyMock is a tool that is able to generate mock implementations of interfaces on the fly in a unit test. Here’s how it works:

  1. You make EasyMock available by using Java 5’s static import to import org.easymock.EasyMock.* (i.e., all of the static methods of EasyMock).

  2. You ask for a mock object. It obliges by returning an object that implements the desired interface (RantService in our case).

  3. You write code to “train” the mock object. This involves making calls to the mock object (as if it were the real object) and telling EasyMock what values to return and what (if any) exceptions to throw.

  4. You then tell the EasyMock that you’re ready to replay the calls and begin your test.

In our test case, we know that RantsForVehicleController will call the getRantsForVehicle() method on the RantService to retrieve a list of rants belonging to that vehicle. Thus, the mock implementation of RantService needs to be able to return a list of rants with the expected values. The setUp() method in listing B.2 creates and trains the mock object to do just that.

Example B.2. Training a mock RantService

After creating the mock RantService, the setUp() method assembles a list of rants that we expect to be returned when getRantsForVehicle() is called on the mock object.

Next, the getRantsForVehicle() method of the RantService is invoked, passing in the test vehicle. Since this is just a training exercise, the value returned from getRantsForVehicle() is unimportant. In fact, the mock object doesn’t even know what to return until we tell it by calling the andReturn() method.

Once the mock object has been trained, we’re ready to start our test. A call to the replay() method tells EasyMock to start expecting calls on the mock object and to respond as it was trained.

The last line of setUp() injects the mock RantService into the rantService property of the controller. RantsForVehicleController is now ready to test.

Asserting the contents of ModelAndView

The testSimpleCase() method is a good start on testing RantsForVehicleController. But it is quite complex with all of the assertions that it makes. It’d be nice if there were a way to streamline those assertions to only a handful that are truly meaningful.

Although the Spring container generally shouldn’t be involved in unit testing, Spring does provide some help for unit-testing controllers in its API. The org.springframework.test.web.AbstractModelAndViewTests class is an extension of JUnit’s TestCase class that exposes some convenient assertion methods (table B.2) for dealing with the contents of a ModelAndView object.

Table B.2. The assertion methods available in AbstractModelAndViewTests.

Assertion method

Purpose

assertAndReturnModelAttributeOfType(ModelAndView mav, Object key, Class type)

Asserts that a model attribute exists at the given key and that it is of a certain type

assertCompareListModelAttribute(ModelAndView mav, Object key,List assertionList)

Asserts that a List model attribute exists at the given key and that it contains the same values (and order) as a given assertion list

assertModelAttributeAvailable(ModelAndView mav, Object key)

Asserts that a model attribute exists at a given key

assertModelAttributeValue(ModelAndView mav, Object key, Object value)

Asserts that the model attribute at a given key is equal to a certain value

assertModelAttributeValues(ModelAndView mav, Map assertionModel)

Asserts that all model attributes match values with a given assertion model Map

assertSortAndCompareListModelAttribute(ModelAndView mav, Object key, List assertionList, Comparator comp)

Same as assertCompareListModelAttribute() except that both lists are sorted before comparing the lists.

assertViewName(ModelAndView mav, name)

Asserts that the given ModelAndView contains a specific view name.

For illustration, let’s adapt RantsForVehicleControllerTest to take advantage of AbstractModelAndViewTests’s assertion methods. First, our test case should be changed to extend AbstractModelAndViewTests:

public class RantsForVehicleControllerTest
    extends AbstractModelAndViewTests {
...
}

Now we’re ready to rewrite testSimpleCase() to use a few of the assertion methods to test the contents of ModelAndView:

public void testSimpleCase() throws Exception {
  MockHttpServletRequest request = new MockHttpServletRequest();
  request.setMethod("POST");
  request.addParameter("state", TEST_STATE);
  request.addParameter("plateNumber", TEST_PLATE_NUMBER);
  request.setRequestURI(
      "http://localhost:8080/roadrantz/rantsForVehicle.htm");

  HttpServletResponse response = new MockHttpServletResponse();

  ModelAndView modelAndView =
      controller.handleRequest(request, response);

  List<Rant> expectedRants = new ArrayList<Rant>();
  Rant rant = new Rant();
  rant.setId(1);
  Vehicle vehicle = new Vehicle();
  vehicle.setState("TX");
  vehicle.setPlateNumber("ABC123");
  rant.setVehicle(vehicle);
  rant.setRantText("This is a test rant");

  assertNotNull("ModelAndView should not be null", modelAndView);
  assertViewName(modelAndView, "vehicleRants");
  assertModelAttributeAvailable(modelAndView, "rants");
  assertCompareListModelAttribute(modelAndView,
      "rants", expectedRants);
}

This new version of testSimpleCase() is greatly simplified from the original. Where there were originally seven assertions (two of which occurred in a loop), there are now only four assertions (and no loop). Despite the fact that there are only half as many assertions, the same things are still being tested:

  • We still know that the returned ModelAndView is not null.

  • We still know that the view name is vehicleRants.

  • We still know that the rants attribute is in the model.

  • We still know that the rants attribute contains a list of the expected rants.

When unit-testing your application objects in isolation, you’ll have little need to wire up all of the objects in your application. Although Spring provides some assistance in writing unit tests, the Spring container stays out of the way.

But that’s unit testing. Test cases that test all of your application objects in concert are still a valid form of testing. For those kinds of tests, the Spring container should be involved to wire your application objects together. When you’re writing those kinds of tests, Spring offers some additional help in the form of JUnit extensions for loading the Spring context. So, let’s switch gears from unit testing and look into the support that Spring provides for integration testing.

Integration testing with Spring

Unit-testing the individual pieces of an application is a good start toward verifying the correctness of software. But it’s just the beginning. Throughout this book, you’ve seen how to use dependency injection and Spring to tie objects together to build applications. Once you know that each of these objects is working, it’s time to test the aggregation of those objects to be sure that they work together. After unit testing, the next phase of testing is integration testing—testing several units of an application in combination.

To find an opportunity for integration testing, look no further than the RoadRantz application. RantsForVehicleController depends on a RantService to do its work. In RantsForVehicleControllerTest, a mock RantService object was injected into the controller in the test’s setUp() method. But when performing integration tests, it is desirable to wire up application objects just like they’ll be wired up in production (or as close as is possible).

It’s quite common to write integration test cases for a Spring application with a setUp() method that resembles the following:

public void setUp throws Exception {
  applicationContext = new FileSystemXmlApplicationContext(
      "roadrantz-service.xml",
      "roadrantz-data.xml");
}

This loads up the Spring application context and sets it to an instance variable for convenient access. Then, as part of the test methods, the applicationContext is used to retrieve one or more beans needed to perform the test. For example, testGetRantsForVehicle() uses applicationContext to retrieve the rantService bean:

public void testGetRantsForVehicle() {
  RantService rantService =
      (RantService) applicationContext.getBean("rantService");

  Vehicle testVehicle = new Vehicle();
  testVehicle.setState(TEST_STATE);
  testVehicle.setPlateNumber(TEST_PLATE_NUMBER);

  List<Rant> rants =
      rantService.getRantsForVehicle(testVehicle);

  assertEquals(2, rants.size());

  for(Iterator iter = rants.iterator(); iter.hasNext(); ) {
    Vehicle vehicle = (Vehicle) iter.next()
    assertEquals(TEST_STATE, vehicle.getState());
    assertEquals(TEST_PLATE_NUMBER, vehicle.getPlateNumber());
  }
}

That’s fine, but it poses a small problem: the nature of setUp() and tearDown() is that they’re respectively called just before and just after each test method. This means that the Spring application context is completely reloaded for every test method in the test case. In a sizable application made up of several objects, this setup could take some time. Over the course of several test methods, it adds up and the test case is slow.

Typically, the beans contained in a Spring application context are stateless. This means that they can’t be tainted by running a test method and that they’re reusable across multiple test methods. So, there’s usually no reason to reload the Spring application context between test methods. In fact, the performance hit usually makes it undesirable to reload the context between test methods.

In support of Spring-based integration testing, Spring provides a handful of test case base classes that handle loading of an application context, ensuring that the context is loaded once and only once:

  • AbstractDependencyInjectionSpringContextTests

  • AbstractTransactionalSpringContextTests

  • AbstractTransactionalDataSourceSpringContextTests

All of these test case classes are found in the org.springframework.test package.

Let’s get started with Spring-based integration testing by looking at the simplest of these, AbstractDependencyInjectionSpringContextTests.

Testing wired objects

Making the job of loading a Spring application context within a test case easier, Spring comes with AbstractDependencyInjectionSpringContextTests (see figure B.3). Aside from having one of the longest names of all of the Spring classes,[2] this class is an extension of JUnit’s TestCase that manages the loading and reloading of Spring application contexts for a test case. It makes the Spring application context available to your test case classes so that it can retrieve beans that are to be tested.

Figure B.3. AbstractDependencyInjectionSpringContextTests is a convenient base class for test cases that loads a Spring application context and makes it available to the test case for accessing beans.

To load Spring application contexts in your unit test cases, your test cases should extend AbstractDependencyInjectionSpringContextTests instead of TestCase. In addition, you should override the getConfigLocations() method to define the location(s) of the Spring configuration XML file(s).

For example, consider AddRantTest (listing B.3). This test case tests the addRant() method of RantService. Instead of testing addRant() in isolation using a mock DAO, this test allows the Spring application context to be loaded and its beans wired in a way that resembles how the production beans will be wired. In fact, as it is using the real roadrantz-data.xml, it will also use a real database.

Example B.3. Testing RantService in the context of the entire RoadRantz application

AbstractDependencyInjectionSpringContextTests supports this test case in two ways. First, it automatically loads up the Spring application context and exposes the context through the applicationContext instance variable. testAddRant() uses the applicationContext variable to retrieve the rantService bean from the context.

Perhaps more importantly, the application context is loaded once and only once, regardless of how many test methods there are. As shown, RantServiceTest only has a single test method. But even if it had 100 test methods, the Spring application context would only be loaded once, improving the overall performance of the test case.

Forcing a context reload

Although it’s unusual, you may encounter a situation where your test taints the Spring application context. Since it’s a best practice to leave the test in a consistent state between test methods, you’ll need a way to force a reload of the Spring context.

Normally, AbstractDependencyInjectionSpringContextTests loads the Spring context only once. But you can force it to reload by calling setDirty () within your test method. When the test method completes, AbstractDependencyInjectionSpringContextTests will see that the Spring context is dirty and will reload a clean context for the next test method to use.

Setting up and tearing down dependency injected tests

You may have noticed that the setUp() method is now gone from RantService-Test. That’s because there’s no longer a need for it, as AbstractDependencyInjectionSpringContextTests handles the loading of the Spring context for us. But in the event that you need a setUp() or a tearDown() method in your test, you should be aware that those methods have been made private so that you will not be able to override them.

The reason why they’ve been made private is because AbstractDependencyInjectionSpringContextTests uses those methods to load and unload the Spring context. If you were to override them, you might accidentally forget to call super.setUp() and super.tearDown() and the application context would not get loaded.

For your own setup and teardown needs, you must override onSetUp() and onTearDown() instead:

public void onSetUp() throws Exception {
  // perform setup work
}
public void onTearDown() throws Exception {
  // perform teardown work
}

Aside from the name change, these methods effectively serve the same purpose as TestCase’s setUp() and tearDown() methods.

AbstractDependencyInjectionSpringContextTests is great for performing integration tests against objects wired together in a Spring application context. But it only covers simple nontransactional cases. When integration tests involve transactional objects, you’ll want to look at Spring’s other unit-test case classes such as AbstractTransactionalSpringContextTests, which we’ll look at next.

Integration-testing transactional objects

As it appears in listing B.3, RantServiceTest does a fine job of testing that Rant-Service’s addRant() actually adds a rant to the database. But it also has one undesirable side effect: it actually adds a rant to the database!

But isn’t that what we want addRant() to do? Yes, but when testing we don’t want that rant to be permanently added to the database. When the test is over, we need the database to return to a known state, ready for the next test. We need the test to roll back the database insert when it’s over.

In addition to AbstractDependencyInjectionSpringContextTests, Spring also comes with AbstractTransactionalSpringContextTests to address the problem of rolling back transactions at the end of a test. If your test cases extend AbstractTransactionalSpringContextTests, a transaction will automatically be started at the beginning of every test method and automatically rolled back at the end. That leaves your test methods free to do whatever they want to the database, knowing that the work will be undone when the test method completes.[3]

To take advantage of AbstractTransactionalSpringContextTests, let’s change RantServiceTest to subclass AbstractTransactionalSpringContextTests. Listing B.4 shows the new RantServiceTest.

Example B.4. Testing addRant() in a transaction

Not much has changed in this version of RantServiceTest. In fact, the only difference that you’ll find is that RantServiceTest now extends AbstractTransactionalSpringContextTests. A lot changes, however, when you run the test. Now no rows are permanently added to the database. Within the context of the test, a row is added for the rant and another row is added for the vehicle (if the vehicle didn’t already exist). But once the test is finished, the changes are rolled back and there will be no trace of this test having been run.

To accommodate the AbstractTransactionalSpringContextTests use of transactions, the application context will need to have a transaction manager bean configured (see chapter 6, section 6.2, for information on Spring’s transaction managers). AbstractTransactionSpringContextTests will look for a transaction manager in the Spring context and use it to create and roll back transactions. The RoadRantz application already has a transaction manager configured in roadrantz-data.xml, so RantServiceTest is ready to go.

Committing the test’s changes

Although it’s unusual, you may want to commit the changes done in a test. If so then you may simply call the setComplete() method within your test method. When the test method completes, the transaction will be committed instead of rolled back. Also, if you’d like to commit the changes before the test method completes, you may call endTransaction() after calling setComplete() to end the transaction and commit the changes immediately.

Be aware of the consequences of setComplete(). That is, any changes made within the test method will be committed after the test completes (or after endTransaction() is called). This means that you’ll need to perform some extra work to clean up the data later or your database will be cluttered with remnants of old tests. This leftover data may get in the way of future tests and make it difficult for your tests to be repeatable.

Setting up and tearing down transactional tests

When working with transactional tests, the notions of setting up before a test and tearing down after a test take on a slight twist. Do you want your setup to occur before or after the transaction starts? Should teardown occur inside the transaction or after the transaction has ended? A single pair of setup and teardown methods is no longer flexible enough.

Therefore, AbstractTransactionalSpringContextTests provides two setup methods and two teardown methods:

  • onSetUpBeforeTransaction()—Called before the transaction starts

  • onSetUpInTransaction()—Called after the transaction starts

  • onTearDownInTransaction()—Called just before the transaction is rolled back (or committed)

  • onTearDownAfterTransaction()—Called after the transaction has been rolled back (or committed)

Just like setUp() and tearDown() in JUnit’s TestCase class or onSetUp() and onTearDown() in Spring’s AbstractDependencyInjectionSpringContextTests, you can define the setup and teardown behavior of your test cases by overriding one or more of these methods. A little later in this appendix, we’ll override the onTearDownAfterTransaction() method to perform some database cleanup.

RantServiceTest is almost done. But there are still a few loose ends to tie up. Let’s see how to give RantServiceTest access to the database itself to assert that the data is being persisted as we expect.

Testing against the database

There’s a small problem with RantServiceTest as it appears in listing B.4. When testing that the rant has been added, we don’t know with any certainty that the rant has been added to the database. The only thing that we can say for sure is that RantService says that a rant has been added to the database.

In a unit test, it’s okay to trust that RantService is telling us the truth because when we’re unit-testing RantService, we’re only concerned that RantService is doing what we expect—not that any database has been updated. But in an integration test, we want to be certain that the database is being updated with the values we expect. Therefore, it’s not good enough to trust RantService. We must go to the database to be certain.

Querying a database with JDBC is not all that complex, but Spring’s JdbcTemplate makes it even easier (as discussed in chapter 5). And AbstractTransactionalDataSourceSpringContextTests[4] makes working with Jdbc-Template even easier when in an integration test.

  • AbstractTransactionalDataSourceSpringContextTests is a transactional test case class like AbstractTransactionalSpringContexTests. But to support database queries, it also provides a jdbcTemplate instance variable. Test methods can use this instance variable to query the database directly.

For example, RantServiceTest has been changed again in listing B.5 to use jdbcTemplate to ensure that the rant has been added to the database.

Example B.5. RantServiceTest modified to query the database to ensure that a rant has been added

Rather than take the word of RantService, RantServiceTest now performs some simple queries against the database. The first assertion made is that the number of rows in the rant table has grown by exactly 1 after the addRant() method is called. The actual rant text is also checked in another assertion.

AbstractTransactionalDataSourceSpringContextTests expects that a DataSource be declared in the Spring application context. That’s not a problem, as we have already defined one in roadrantz-data.xml.

Cleaning up

Although AbstractTransactionalDataSourceSpringContextTests will roll back any changes made in the test methods, it’s still possible for you to force a commit by calling setComplete(). Even if you have good reasons for committing the data in the course of your tests, you’ll probably still want to clean up the mess when you’re done.

To accommodate test data cleanup, AbstractTransactionalDataSourceSpringContextTests provides a convenience method for deleting all data in one or more tables. The deleteFromTables() method deletes all rows from the tables specified in a String array.

To illustrate, here’s an onTearDownAfterTransaction() method that calls deleteFromTables() to delete everything in the rant and vehicle tables:

protected void onTearDownAfterTransaction() throws Exception {
  deleteFromTables(new String[] {"rant", "vehicle"});
}

It’s important to understand the consequences of calling deleteFromTables(). All data will be deleted from the tables specified—not just the data that was added by the test. Therefore, it’s important that you only use deleteFromTables() when testing against a database where you can afford to wipe the tables clean. (In other words, please don’t run these kinds of tests against your production database!)

Testing in JUnit 4 with Gienah Testing

All of the examples in this appendix so far have been based on JUnit 3.8.1, as that’s the version of JUnit that Spring’s abstract test case classes are based on. Even so, as I write this, JUnit 4.3.1 is the latest version of JUnit available, and JUnit 4.4 is expected to be released very soon. JUnit 4 takes advantage of Java 5 annotations in defining test cases, resulting in slightly cleaner testing code.

In case you’d like to move up to JUnit 4, I thought I should inform you of gienah-testing (http://code.google.com/p/gienah-testing/), a new JUnit 4 extension that enables dependency injection of JUnit 4 test classes. It uses a custom JUnit 4 runner to load a Spring application context and inject values into class-scoped variables in the test case class.

To demonstrate gienah-testing, let’s rewrite the RantServiceTest test class, basing it on JUnit 4 and using gienah-testing to inject the RantService directly into the test class’s rantService variable. Listing B.6 shows the new version of RantServiceTest.

Example B.6. The RantServiceTest, rewritten as a JUnit 4 test case, using gienah-testing

The first thing to notice about this new JUnit 4/gienah-based test case is that the class is annotated with @RunWith to use SpringRunner. JUnit 4 uses test runners to know how to execute tests. JUnit 4’s default test runner knows nothing about Spring, so here @RunWith specifies that gienah’s Spring-aware test runner be used instead.

Now that JUnit 4 knows to use the Spring-aware test runner, it will need to know which Spring context definition files to load. For that, I’m using gienah’s @Configuration annotation to load roadrantz-services.xml and roadrantzdata.xml.

In the previous versions of RantServiceTest, the RantService instance was retrieved directly from the Spring application context. But with gienah, it can be injected from the Spring context. The @Dependency annotation tells gienah to inject the rantService variable with a bean from the Spring context whose name is rantService (the same name as the variable). Figure B.4 illustrates how this works.

Figure B.4. gienah-testing’s SpringRunner automatically injects dependency variables in a JUnit 4 test case with beans from the Spring application context.

If for some reason the RantService was named something else (perhaps road-RantService), I could specify a bean name using the bean attribute:

@Dependency(bean="roadRantService")
private RantService rantService;

Finally, the @Transactional annotation placed on the test method indicates that any work done within the test method should occur within a transaction and be rolled back after the test completes. This keeps the test method from leaving a mess behind when it’s over.

gienah-testing brings much of Spring’s context-aware testing into the JUnit 4 world. But you should be aware that as I write this, gienah-testing is at version E0.31, where “E” stands for “experimental,” and E0.4 is to be released at the same time as JUnit 4.4. As it is experimental, you should expect some quirks. Here are a couple that I’ve encountered:

  • The @Transactional annotation depends on a transaction manager being configured in the Spring application context with a bean ID of transactionManager. It will not work if the transaction manager is configured with any other ID.

  • Oddly, the E0.31 JAR file is compiled using a Java 6 compiler. If you want to use gienah-testing with Java 5, you’re out of luck. This is unfortunate as it shuts out anyone who can’t use Java 6 yet (such as Mac users).

Both of these issues are expected to be fixed with E0.4. Even so, E0.4 is still an experimental release—so proceed with caution.

One other annoyance is that gienah-testing is not currently available in any of the known Maven 2 repositories. Therefore, if you’re using Maven 2 for your build (as I am), you’ll need to download gienah-testing and add it to your local repository using the install:install-file goal:

% mvn install:install-file
         -DgroupId=gienah
         -DartifactId=gienah
         -Dversion=E0.31-patched
         -Dpackaging=jar
         -Dfile=gienah-testing-bin-E0.31-patched.jar

Despite its quirkiness, gienah-testing shows a lot of promise. Keep an eye on its progress at its homepage: http://code.google.com/p/gienah-testing.

Summary

Test-driven development is a practical way of ensuring the correctness of the code you write. Testing frameworks like JUnit are indispensable in writing and running automated test cases.

Although Spring supports unit testing by encouraging decoupled code, Spring isn’t often directly involved in the activity of testing. Nevertheless, there are some circumstances where Spring can pitch in and make testing a bit easier.

For unit testing, Spring’s AbstractModelAndViewTests supplies developers with some handy assertion methods that make short work of verifying the data returned from a Spring MVC controller in a ModelAndView object. And Spring also comes with several mock implementations of common interfaces that are perfect for testing controller classes.

When it comes to integration testing, AbstractDependencyInjectionSpringContextTests can handle loading and unloading of the Spring application context. AbstractTransactionalSpringContextTests, along with AbstractTransactionalDataSourceSpringContextTests, can also ensure that any database work is rolled back between tests.



[1] Note that we’re using EasyMock 2.0, which takes advantage of Java 5’s support for static imports. If you’re using an older version of Java, you’ll need to use an older version of EasyMock that doesn’t use static imports.

[2] It’s not the longest, though. Before this appendix is finished, you’ll see an even longer class name.

[3] All of this assumes that the underlying database supports transactions. If you’re using MySQL MyISAM tables, for example, your database tables do not support transactions and thus AbstractTransactionalSpringContextTests won’t be able to apply transactions to the tests.

[4] Believe it or not, AbstractTransactionalDataSouceSpringContextTests is still not the class with the longest name in the Spring API. A quick scan of Spring’s JavaDoc reveals that LazySingletonMetadataAwareAspectInstanceFactoryDecorator takes top prize for having the longest class name. What does that class do? Well... isn’t it obvious from the name?