From 2b7a62906891b7a936412894f9ab78cdb69dc378 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sat, 28 Jul 2012 01:24:32 +0200 Subject: [PATCH] Support TransactionManagementConfigurer in the TCF Currently the Spring TestContext Framework looks up a PlatformTransactionManager bean named "transactionManager". The exact name of the bean can be overridden via @TransactionConfiguration or @Transactional; however, the bean will always be looked up 'by name'. The TransactionManagementConfigurer interface that was introduced in Spring 3.1 provides a programmatic approach to specifying the PlatformTransactionManager bean to be used for annotation-driven transaction management, and that bean is not required to be named "transactionManager". However, as of Spring 3.1.2, using the TransactionManagementConfigurer on a @Configuration class has no effect on how the TestContext framework looks up the transaction manager. Consequently, if an explicit name or qualifier has not been specified, the bean must be named "transactionManager" in order for a transactional integration test to work. This commit addresses this issue by refactoring the TransactionalTestExecutionListener so that it looks up and delegates to a single TransactionManagementConfigurer as part of the algorithm for determining the transaction manager. Issue: SPR-9604 --- .../transaction/TransactionConfiguration.java | 17 ++- .../TransactionalTestExecutionListener.java | 35 ++++-- ...aTransactionManagementConfigurerTests.java | 101 ++++++++++++++++++ .../spr9645/LookUpNonexistentTxMgrTests.java | 20 +--- .../LookUpTxMgrByTypeAndDefaultNameTests.java | 2 +- .../LookUpTxMgrByTypeAndNameTests.java | 2 +- ...grByTypeAndQualifierAtClassLevelTests.java | 2 +- ...rByTypeAndQualifierAtMethodLevelTests.java | 2 +- .../spr9645/LookUpTxMgrByTypeTests.java | 2 +- src/dist/changelog.txt | 1 + src/reference/docbook/testing.xml | 15 +-- 11 files changed, 155 insertions(+), 44 deletions(-) create mode 100644 spring-test/src/test/java/org/springframework/test/context/junit4/spr9604/LookUpTxMgrViaTransactionManagementConfigurerTests.java diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java index e62317b783..7d91bcba02 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java @@ -40,12 +40,19 @@ public @interface TransactionConfiguration { /** * The bean name of the {@link org.springframework.transaction.PlatformTransactionManager - * PlatformTransactionManager} that is to be used to drive transactions. + * PlatformTransactionManager} that should be used to drive transactions. * - *

This attribute is not required and only needs to be specified explicitly - * if there are multiple beans of type {@code PlatformTransactionManager} in - * the test's {@code ApplicationContext} and the bean name of the desired - * {@code PlatformTransactionManager} is not "transactionManager". + *

This attribute is not required and only needs to be declared if there + * are multiple beans of type {@code PlatformTransactionManager} in the test's + * {@code ApplicationContext} and if one of the following is true. + *

* *

NOTE: The XML {@code } element also refers * to a bean named "transactionManager" by default. If you are using both diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java index e182259ba7..9b1c529321 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java @@ -43,6 +43,7 @@ import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; +import org.springframework.transaction.annotation.TransactionManagementConfigurer; import org.springframework.transaction.interceptor.DelegatingTransactionAttribute; import org.springframework.transaction.interceptor.TransactionAttribute; import org.springframework.transaction.interceptor.TransactionAttributeSource; @@ -68,11 +69,15 @@ import org.springframework.util.StringUtils; * *

Transactional commit and rollback behavior can be configured via the * class-level {@link TransactionConfiguration @TransactionConfiguration} and - * method-level {@link Rollback @Rollback} annotations. In case there are multiple - * instances of {@code PlatformTransactionManager} within the test's - * {@code ApplicationContext}, {@code @TransactionConfiguration} supports - * configuring the bean name of the {@code PlatformTransactionManager} that is - * to be used to drive transactions. + * method-level {@link Rollback @Rollback} annotations. + * + *

In case there are multiple instances of {@code PlatformTransactionManager} + * within the test's {@code ApplicationContext}, @{@code TransactionConfiguration} + * supports configuring the bean name of the {@code PlatformTransactionManager} + * that should be used to drive transactions. Alternatively, + * {@link TransactionManagementConfigurer} can be implemented in an + * {@link org.springframework.context.annotation.Configuration @Configuration} + * class. * *

When executing transactional tests, it is sometimes useful to be able to * execute certain set up or tear down code outside of a @@ -349,13 +354,25 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis return bf.getBean(tmName, PlatformTransactionManager.class); } - // look up single bean by type if (bf instanceof ListableBeanFactory) { ListableBeanFactory lbf = (ListableBeanFactory) bf; - Map beansOfType = BeanFactoryUtils.beansOfTypeIncludingAncestors( + + // look up single bean by type + Map txMgrs = BeanFactoryUtils.beansOfTypeIncludingAncestors( lbf, PlatformTransactionManager.class); - if (beansOfType.size() == 1) { - return beansOfType.values().iterator().next(); + if (txMgrs.size() == 1) { + return txMgrs.values().iterator().next(); + } + + // look up single TransactionManagementConfigurer + Map configurers = BeanFactoryUtils.beansOfTypeIncludingAncestors( + lbf, TransactionManagementConfigurer.class); + if (configurers.size() > 1) { + throw new IllegalStateException( + "Only one TransactionManagementConfigurer may exist in the ApplicationContext"); + } + if (configurers.size() == 1) { + return configurers.values().iterator().next().annotationDrivenTransactionManager(); } } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9604/LookUpTxMgrViaTransactionManagementConfigurerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9604/LookUpTxMgrViaTransactionManagementConfigurerTests.java new file mode 100644 index 0000000000..de9e21b2e3 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9604/LookUpTxMgrViaTransactionManagementConfigurerTests.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.junit4.spr9604; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.transaction.AfterTransaction; +import org.springframework.test.context.transaction.BeforeTransaction; +import org.springframework.test.transaction.CallCountingTransactionManager; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.TransactionManagementConfigurer; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests that verify the behavior requested in + * SPR-9604. + * + * @author Sam Brannen + * @since 3.2 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@Transactional +public class LookUpTxMgrViaTransactionManagementConfigurerTests { + + private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager(); + private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager(); + + + @Configuration + static class Config implements TransactionManagementConfigurer { + + public PlatformTransactionManager annotationDrivenTransactionManager() { + return txManager1(); + } + + @Bean + public PlatformTransactionManager txManager1() { + return txManager1; + } + + @Bean + public PlatformTransactionManager txManager2() { + return txManager2; + } + } + + + @BeforeTransaction + public void beforeTransaction() { + txManager1.clear(); + txManager2.clear(); + } + + @Test + public void transactionalTest() { + assertEquals(1, txManager1.begun); + assertEquals(1, txManager1.inflight); + assertEquals(0, txManager1.commits); + assertEquals(0, txManager1.rollbacks); + + assertEquals(0, txManager2.begun); + assertEquals(0, txManager2.inflight); + assertEquals(0, txManager2.commits); + assertEquals(0, txManager2.rollbacks); + } + + @AfterTransaction + public void afterTransaction() { + assertEquals(1, txManager1.begun); + assertEquals(0, txManager1.inflight); + assertEquals(0, txManager1.commits); + assertEquals(1, txManager1.rollbacks); + + assertEquals(0, txManager2.begun); + assertEquals(0, txManager2.inflight); + assertEquals(0, txManager2.commits); + assertEquals(0, txManager2.rollbacks); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpNonexistentTxMgrTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpNonexistentTxMgrTests.java index 0a70b0d0a2..72af7e7a7b 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpNonexistentTxMgrTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpNonexistentTxMgrTests.java @@ -24,8 +24,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.transaction.AfterTransaction; -import org.springframework.test.context.transaction.BeforeTransaction; import org.springframework.test.transaction.CallCountingTransactionManager; import org.springframework.transaction.PlatformTransactionManager; @@ -42,7 +40,6 @@ public class LookUpNonexistentTxMgrTests { private static final CallCountingTransactionManager txManager = new CallCountingTransactionManager(); - @Configuration static class Config { @@ -52,26 +49,11 @@ public class LookUpNonexistentTxMgrTests { } } - - @BeforeTransaction - public void beforeTransaction() { - txManager.clear(); - } - @Test - public void lookUpNothing() { + public void nonTransactionalTest() { assertEquals(0, txManager.begun); assertEquals(0, txManager.inflight); assertEquals(0, txManager.commits); assertEquals(0, txManager.rollbacks); } - - @AfterTransaction - public void afterTransaction() { - assertEquals(0, txManager.begun); - assertEquals(0, txManager.inflight); - assertEquals(0, txManager.commits); - assertEquals(0, txManager.rollbacks); - } - } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndDefaultNameTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndDefaultNameTests.java index 5cfdebaba3..6f1525229f 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndDefaultNameTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndDefaultNameTests.java @@ -66,7 +66,7 @@ public class LookUpTxMgrByTypeAndDefaultNameTests { } @Test - public void lookUpByTypeAndDefaultName() { + public void transactionalTest() { assertEquals(1, txManager1.begun); assertEquals(1, txManager1.inflight); assertEquals(0, txManager1.commits); diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndNameTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndNameTests.java index 805243f11c..d8c070c504 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndNameTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndNameTests.java @@ -68,7 +68,7 @@ public class LookUpTxMgrByTypeAndNameTests { } @Test - public void lookUpByTypeAndName() { + public void transactionalTest() { assertEquals(1, txManager1.begun); assertEquals(1, txManager1.inflight); assertEquals(0, txManager1.commits); diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtClassLevelTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtClassLevelTests.java index cd78baea45..bbc0951e4f 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtClassLevelTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtClassLevelTests.java @@ -66,7 +66,7 @@ public class LookUpTxMgrByTypeAndQualifierAtClassLevelTests { } @Test - public void lookUpByTypeAndQualifier() { + public void transactionalTest() { assertEquals(1, txManager1.begun); assertEquals(1, txManager1.inflight); assertEquals(0, txManager1.commits); diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtMethodLevelTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtMethodLevelTests.java index 0a9335dbca..b9d6b4b6ae 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtMethodLevelTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtMethodLevelTests.java @@ -66,7 +66,7 @@ public class LookUpTxMgrByTypeAndQualifierAtMethodLevelTests { @Transactional("txManager1") @Test - public void lookUpByTypeAndQualifier() { + public void transactionalTest() { assertEquals(1, txManager1.begun); assertEquals(1, txManager1.inflight); assertEquals(0, txManager1.commits); diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeTests.java index ed48479805..b7a52aa961 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeTests.java @@ -59,7 +59,7 @@ public class LookUpTxMgrByTypeTests { } @Test - public void lookUpByType() { + public void transactionalTest() { assertEquals(1, txManager.begun); assertEquals(1, txManager.inflight); assertEquals(0, txManager.commits); diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index 4c8f1c6443..485b040f9f 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -29,6 +29,7 @@ Changes in version 3.2 M2 (2012-08-xx) * support content negotiation options in MVC namespace and MVC Java config * support named dispatchers in MockServletContext (SPR-9587) * support single, unqualified tx manager in the TestContext framework (SPR-9645) +* support TransactionManagementConfigurer in the TestContext framework (SPR-9604) Changes in version 3.2 M1 (2012-05-28) diff --git a/src/reference/docbook/testing.xml b/src/reference/docbook/testing.xml index 0f8a4bf064..ed0f0499aa 100644 --- a/src/reference/docbook/testing.xml +++ b/src/reference/docbook/testing.xml @@ -630,9 +630,9 @@ public class CustomTestExecutionListenerTests { Defines class-level metadata for configuring transactional tests. Specifically, the bean name of the - PlatformTransactionManager that is - to be used to drive transactions can be explicitly specified if - there are multiple beans of type + PlatformTransactionManager that + should be used to drive transactions can be explicitly specified + if there are multiple beans of type PlatformTransactionManager in the test's ApplicationContext and the bean name of the desired @@ -657,9 +657,12 @@ public class CustomConfiguredTransactionalTests { configuration, you can avoid using @TransactionConfiguration altogether. In other words, if you have only one transaction - manger (or your transaction manager bean is named - "transactionManager") and if you want transactions to roll back - automatically, there is no need to annotate your test class with + manger — or if you have multiple transaction mangers but the + transaction manager for tests is named "transactionManager" or + specified via a + TransactionManagementConfigurer — + and if you want transactions to roll back automatically, then + there is no need to annotate your test class with @TransactionConfiguration.