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.