Section 7.6. Building a Test-Friendly Interceptor

team bbl


7.6. Building a Test-Friendly Interceptor

In this example, you'll learn how to build interceptors that do nothing but improve your ability to test code. Usually, a transaction interceptor does three things:

  1. Begins a transaction when a method starts.

  2. Rolls back a transaction when an exception is thrown.

  3. Commits the transaction when a method completes.

Of course, that's what you usually want to happen. Test cases are different animals, though. They're responsible for putting conditions back to the state where they were before a test case was fired, and they need to execute quickly.

For a test case, often it makes sense to replace the third step. You may instead want the transaction to roll back after the method completes. That way, you remove one of the most expensive database steps (the commit), and you also restore the database state to what it was before you executed the transaction.

7.6.1. How do I do that?

In Spring, it's easy to build this type of behavior into an interceptor, because you can build a custom interceptor. Example 7-29 shows the code for a transaction interceptor that rolls back.

Example 7-29. TestTxInterceptor.java
public class TestTxInterceptor implements MethodInterceptor {     private PlatformTransactionManager platformTransactionManager;     public PlatformTransactionManager getPlatformTransactionManager( ) {         return platformTransactionManager;     }     public void setPlatformTransactionManager(PlatformTransactionManager        platformTransactionManager) {         this.platformTransactionManager = platformTransactionManager;     }     public Object invoke(MethodInvocation methodInvocation)        throws Throwable {         DefaultTransactionDefinition def =            new DefaultTransactionDefinition( );         def.setPropagationBehavior(TransactionDefinition.           PROPAGATION_REQUIRED);         TransactionStatus status =            platformTransactionManager.getTransaction(def);                  Object results = null;         try {             results = methodInvocation.proceed( );         } finally {             platformTransactionManager.rollback(status);         }         return results;     } }

Next, Example 7-30 shows the configuration of the context.

Example 7-30. RentABikeApp-Servlet.xml
<bean      >    <property name="sessionFactory"><ref local="sessionFactory"/></property> </bean> <bean      >    <property name="platformTransactionManager">       <ref local="transactionManager"/>    </property> </bean> <bean      >    <property name="proxyInterfaces">       <value>com.springbook.RentABike</value>    </property>    <property name="interceptorNames">          <list>             <idref local="bikeRentalSecurity"/>             <idref local="testTxInterceptor"/>             <idref local="rentaBikeTarget"/>          </list>    </property> </bean>

Now, you can write any JUnit tests you want against the façade layer, testing reads and writes, testing for appropriate security filtering, and so on, without worrying about changing the database. When running the unit tests, use a version of the configuration file that assigns the interceptor, and leave out the interceptor in a deployment scenario.

7.6.2. What just happened?

The typical JUnit flow works like this:

  • setUp prepares the system for a test case. In our setUp method, we do all of the work that's common across the databases.

  • A series of individual tests then follow. In our case, we slightly change the flow, in order to call all of the methods within a method that uses the Spring context.

tearDown then does all cleanup. For database tests, the tearDown can be particularly expensive, because it's got to restore the test data to a usable state for other test cases. (An alternative approach is the reset the database to a known good state in every setUp method. If the set of test data is small enough, this is a safer approach. The larger the set of test data, the more likely it is that you will want to use tearDown to undo test changes instead of setUp to recreate the full database.) A test changes something in the database, verifies the results, and then repeats. Notice also that all of the database access is in a central method. That makes it easy to bracket the method with declarative transactions.

Now look at the context. This is a case where you're actually proxying the test code to add some behavior that makes testing easier. That's using the power of Spring to build a much better test. In the next chapter, you'll look into a few more services, like remoting and messaging.

    team bbl



    Spring. A developer's Notebook
    Spring: A Developers Notebook
    ISBN: 0596009100
    EAN: 2147483647
    Year: 2005
    Pages: 90

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net