Writing Integration Tests

Integration tests may sound too complex, but a simple integration test is a data access layer implementation test that actually connects to the database and accesses the data in the appropriate tables. Such a test requires interaction of the database, the Spring framework, and the code being tested. If any part of this chain fails, the entire test fails.

Manually creating beans for an integration test may be too difficult and unnecessary to code manually. Instead, we create the Spring application context and let the framework instantiate the beans for us. In most cases, you will be using XmlApplicationContext, thus you need to instantiate one of the XmlApplicationContext implementations. Let's take a look at the code in Listing A-5 to see how we can go about this.

Listing A-5: Creating the Application Context

image from book
public class TransactionTest extends TestCase {          private ApplicationContext getApplicationContext() {         String[] paths = new String[] {                 "./business/src/resources/applicationContext-db.xml",                 "./business/src/resources/applicationContext.xml" };         return new FileSystemXmlApplicationContext(paths);     }      }
image from book

The paths[] array specifies the file that is read to build the application context; the actual implementation we use is FileSystemXmlApplicationContext. We can then use the application context to programmatically get instances of the beans we need to test. Let's take a look at how we implemented the database transaction test in the sample application in Listing A-6.

Listing A-6: TransactionTest Test Case

image from book
package com.apress.prospring.business;      import java.util.Date;      import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;      import com.apress.prospring.domain.Entry; import com.apress.prospring.domain.User;      import junit.framework.TestCase;      public class TransactionTest extends TestCase {          private ApplicationContext getApplicationContext() {         String[] paths = new String[] {                 "./business/src/resources/applicationContext-db.xml",                 "./business/src/resources/applicationContext.xml" };              return new FileSystemXmlApplicationContext(paths);     }          public void testTransaction() {         ApplicationContext ctx = getApplicationContext();         DefaultBlogManager bm =              (DefaultBlogManager) ctx.getBean("blogManager");         bm.setAuditService(new MockAuditService());              int countBefore = bm.getAllEntries().size();         Entry e = new Entry();         e.setSubject("Tester");         e.setBody("Body");         e.setPostDate(new Date());              try {             bm.saveEntry(e);             fail("Should have thrown RuntimeException");         } catch (RuntimeException ex) {              }         int countAfter = bm.getAllEntries().size();         assertEquals("The new Entry should not have been added", countBefore,                 countAfter);          }          private static class MockAuditService implements AuditService {         public void writeAuditMessage(String data, User user) {             throw new RuntimeException("Foo!");         }         public void purgeAudit(Date oldestDate) {         }     } }
image from book

In this test, we used Spring to get an instance of BlogManager, but we have set its auditService property to a mock implementation of the AuditService. The mock implementation always throws an exception in its writeAuditMessage() method, which must cause the BlogManager.saveEntry() work to be rolled back. We test this by getting the total number of entries, and then calling the saveEntry() method, failing the test if the saveEntry() method succeeds, and finally checking the new number of entries, which must be the same as the number of entries before we called the saveEntry() method.

Unfortunately, this type of testing is only going to work when you are testing local transactions. If your application must be run in an application server because it uses JTA transactions or any other services provided by the application server, your only option is to use some kind of EJB unit testing framework, such as Cactus (http://jakarta.apache.org/cactus). This type of testing is well beyond the scope of this appendix.

Since version 1.1.1, Spring has come up with a collection of convenience superclasses that simplify the creation of integration tests. All these classes are packaged in the spring-mock.jar file and extend the TestCase class; they are summarized in Table A-1.

Table A-1: TestCase Subclasses in a Spring Mock Package

Class

Description

AbstractSpringContextTests

Maintains a map of Spring contexts so that they are not re-created for each JUnit test. The getContext() method returns and caches either context loaded in the loadContextLocations() method if the argument is instanceof String[] or context loaded in the loadContext() method if the argument is not instanceof String[].

AbstractDependencyInjectionSpringContextTests

Subclass of AbstractSpringContextTests. When used as a superclass of your test, it allows the Spring beans to be set using either setters (default strategy) or protected fields. You no longer have to manually create the application context and get the bean you wish to test from it.

AbstractTransactionalSpringContextTests

A subclass of AbstractDependencyInjectionSpringContext- Tests that makes sure the work performed as part of each test is automatically rolled back when the test finishes. This is useful if you want to maintain a consistent state of the database between tests.

AbstractTransactionalDataSourceSpringContextTests

Convenience subclass of AbstractTransactionalSpringContextTests that allows the tables that are affected in the test to be cleared by executing a delete from SQL command.

Now that you know the classes available in a Spring mock package, take a look at Listing A-7, which shows how we can refactor the TransactionTest class.

Listing A-7: Refactored TransactionTest

image from book
package com.apress.prospring.business;      import java.util.Date; import java.util.List;      import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.test.AbstractDependencyInjectionSpringContextTests;      import com.apress.prospring.domain.Entry; import com.apress.prospring.domain.User;      public class TransactionTest2      extends AbstractDependencyInjectionSpringContextTests {          private DefaultBlogManager blogManager;          protected ConfigurableApplicationContext loadContextLocations(         String[] paths) {         return new FileSystemXmlApplicationContext(paths);     }          public void setBlogManager(DefaultBlogManager blogManager) {         this.blogManager = blogManager;     }          protected String[] getConfigLocations() {         return new String[] { "./business/src/resources/applicationContext-db.xml",             "./business/src/resources/applicationContext.xml",             "./business/src/resources/applicationContext-jdbc.xml"};     }          public void testTransaction2() {          // use the blogManager as usual     }          private static class MockAuditService implements AuditService { }      }
image from book

As you can see from Listing A-7, we removed the code that creates the application context and looks up the blogManager bean. Instead, we use the AbstractDependencyInjectionSpringContextTests as a superclass of our test case, create a setter for the blogManager property, override the loadContextLocations() method, and implement the abstract getConfigLocations() method to make sure the context files are loaded properly. The rest of the code in this test performs the same actions as the test shown in Listing A-6.

Another aspect of integration testing is represented by the Web Tier tests. Generally, you could use HttpUnit (http://httpunit.sourceforge.net/) to write the tests, but Spring allows you to get instances of the controller beans and then use classes from the org.springframework.mock.web package to write tests for the Web Tier without having to use a web server.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

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