Testing with Spring 2.5

Besides other cool features, Spring 2.5 brought completely rewritten support for functional tests. Before this version, Spring had support for functional tests using JUnit 3 only. If you needed to use JUnit 4 or TestNG you just did not get any help from the framework

Now you can use brand new annotations and you are not constrained in your choice of testing framework any more. For complete information, please consult Spring reference, I will show only small subset of the new features. Let's take a look on the first example.



01 @RunWith(SpringJUnit4ClassRunner.class)
02 @ContextConfiguration(locations="classpath:applicationContext.xml")
03 @Transactional
04 public class TestJpaClientDao {
05 
06   private static final long PERSONAL_NUM = 123L;
07   @Autowired
08   private ClientDao clientDao;
09   
10   @Test
11   public void testCreateClient()
12   {
13     Client client = new Client(PERSONAL_NUM);
14     client.setName("John Doe");
15     client.addAddress(new Address("Old st.","2a","Prague","120 00"));
16     client.addAccount(new Account("123-4560789"));
17     client.addAccount(new Account("888-8888888"));
18     clientDao.createClient(client);
19     
20     Client loadedClient = clientDao.loadClientWithPersonalNumber(PERSONAL_NUM);
21     assertSame(client, loadedClient);
22   }

Here we see normal class with some annotations. But it is not normal class, it is JUnit 4 test. By RunWith annotation we are extending usual JUnit test runner in order to enable Spring support. Then we have to define where application context XML file(s) are (@ContextConfiguration annotation). Now we can use Autowire annotations to have all necessary dependencies injected. If we use annotation Transactional we obtain similar behavior as when we were using old AbstractTransactionalSpringContextTests. It means:

  1. The application context is shared for all test methods (in all test classes). So all the stuff is initialized only once. If your test does some changes to the application context and it has to be therefore discarded, you can use DirtiesContext annotation.
  2. All test methods are run in transaction which is rolled back at the end of every test method. You can change this behavior by Rollback annotation or by TransactionConfiguration annotation.

The new Spring test support is great. I just miss one thing. Old test support classes gave me possibility to finish and start a transaction in the middle of the test method (by calling setComplete, finishTransaction and startNewTransaction). I agree, that you do not need such feature very often, but sometimes it is handy. I was not able to find something similar in the new support classes. Finally I found a solution but it is not so straightforward as the old one (If you know better one please inform me).

In the following example I am trying to test whether my DAO is fetching addresses of the client so I do not get LazyInit exception. In my test I need to store a client to the database, finish the transaction, then load the client from the DB in a new transaction and finally check if the addresses are accessible even when no transaction is active.



01   @Test
02   @NotTransactional
03   public void testCreateAndLoadInTwoTransactions()
04   {
05     final Client client = new Client(PERSONAL_NUM);
06     client.setName("John Doe");
07     client.addAddress(new Address("Old st.","2a","Prague","120 00"));
08     client.addAccount(new Account("123-4560789"));
09     client.addAccount(new Account("888-8888888"));
10     getTransactionTemplate().execute(new TransactionCallback(){
11       public Object doInTransaction(TransactionStatus status) {
12         return clientDao.createClient(client);
13       }
14       
15     });
16     Client loadedClient = (ClientgetTransactionTemplate().execute(new TransactionCallback(){
17       public Object doInTransaction(TransactionStatus status) {
18         return clientDao.loadClientWithPersonalNumber(PERSONAL_NUM);
19       }
20     });
21     assertEquals(2,loadedClient.getAccounts().size());
22     
23     //cleanup
24     getTransactionTemplate().execute(new TransactionCallback(){
25       public Object doInTransaction(TransactionStatus status) {
26         clientDao.deleteClient(client.getId());
27         return null;
28       }
29       
30     });
31     
32   }
33   private TransactionTemplate getTransactionTemplate() {
34     return new TransactionTemplate(transactionManager);
35   }

As you can see, I have marked the method as NonTransactional. Therefore, Spring does not create a transaction for me and I can manage my transaction programmatically. For example using Spring transaction template. And that's it.

The source code can be downloaded from SVN repository here.

Note: If you wonder why the hell I have started to write in something that looks almost like English when apparently I do even more mistakes than in Czech, the answer is simple. I just need to practice my English (apart from that I want to be world famous, not just known in Czech Republic)

Tags: , ,

5 Responses to “Testing with Spring 2.5”

  1. Dagi Says:

    This is one of the coolest feature of the 2.5 release. I really appreciate the annotation approach that dramatically reduces amount of a "glue" code. There is also simplified way for extensions of the test execution behavior, for details see a chapter "8.3.7.1. Key abstractions" in the documentation.

  2. Phil Wilson Says:

    Excellent article, very useful indeed, thanks!

  3. Matt Raible Says:

    How do you get a reference to transactionManager? Do you @Autowire it into your Test class?

  4. Lukáš Křečan Says:

    Yes, it is autowired full source code is here

  5. Alex Says:

    Thx !
    info about TransactionTemplate really useful )