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 c2c72c7c3444d4bf805cf633e936fb2d800ee122..e62317b783a84f8906bb4174f117d24b3daf2a2f 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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. @@ -23,17 +23,14 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.transaction.PlatformTransactionManager; - /** - * TransactionConfiguration defines class-level metadata for configuring + * {@code TransactionConfiguration} defines class-level metadata for configuring * transactional tests. * * @author Sam Brannen * @since 2.5 - * @see ContextConfiguration * @see TransactionalTestExecutionListener + * @see org.springframework.test.context.ContextConfiguration */ @Documented @Inherited @@ -42,15 +39,19 @@ import org.springframework.transaction.PlatformTransactionManager; public @interface TransactionConfiguration { /** - * The bean name of the {@link PlatformTransactionManager} that is to be - * used to drive transactions. This attribute is not required and only needs - * to be specified explicitly if the bean name of the desired - * PlatformTransactionManager is not "transactionManager". - *

NOTE: The XML <tx:annotation-driven> element - * also refers to a bean named "transactionManager" by default. If you are - * using both features in combination, make sure to point to the same - * transaction manager bean - here in @TransactionConfiguration and - * also in <tx:annotation-driven transaction-manager="...">. + * The bean name of the {@link org.springframework.transaction.PlatformTransactionManager + * PlatformTransactionManager} that is to 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". + * + *

NOTE: The XML {@code } element also refers + * to a bean named "transactionManager" by default. If you are using both + * features in combination, make sure to point to the same transaction manager + * bean - here in {@code @TransactionConfiguration} and also in + * {@code }. */ String transactionManager() default "transactionManager"; 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 2e533ad4638544937bcbdc93bd827f89b8fffe98..e182259ba726d1aebf33187be6063eb856b8e296 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 @@ -19,7 +19,6 @@ package org.springframework.test.context.transaction; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; - import java.util.ArrayList; import java.util.Collections; import java.util.IdentityHashMap; @@ -30,7 +29,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; +import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.test.annotation.NotTransactional; import org.springframework.test.annotation.Rollback; @@ -49,37 +51,35 @@ import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** - *

- * TestExecutionListener that provides support for executing - * tests within transactions by using the + * {@code TestExecutionListener} that provides support for executing tests + * within transactions by honoring the * {@link org.springframework.transaction.annotation.Transactional @Transactional} - * and {@link NotTransactional @NotTransactional} annotations. - *

- *

- * Changes to the database during a test that is run with @Transactional will be - * run within a transaction that will, by default, be automatically + * and {@link NotTransactional @NotTransactional} annotations. Expects a + * {@link PlatformTransactionManager} bean to be defined in the Spring + * {@link ApplicationContext} for the test. + * + *

Changes to the database during a test that is run with {@code @Transactional} + * will be run within a transaction that will, by default, be automatically * rolled back after completion of the test; whereas, changes to the - * database during a test that is run with @NotTransactional will not - * be run within a transaction. Test methods that are not annotated with either - * @Transactional (at the class or method level) or @NotTransactional - * will not be run within a transaction. - *

- *

- * Transactional commit and rollback behavior can be configured via the - * class-level {@link TransactionConfiguration @TransactionConfiguration} and - * method-level {@link Rollback @Rollback} annotations. - * {@link TransactionConfiguration @TransactionConfiguration} also provides - * configuration of the bean name of the {@link PlatformTransactionManager} that - * is to be used to drive transactions. - *

- *

- * When executing transactional tests, it is sometimes useful to be able to execute - * certain set up or tear down code outside of a - * transaction. TransactionalTestExecutionListener provides such + * database during a test that is run with {@code @NotTransactional} will + * not be run within a transaction. Test methods that are not + * annotated with either {@code @Transactional} (at the class or method level) + * or {@code @NotTransactional} will not be run within a transaction. + * + *

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. + * + *

When executing transactional tests, it is sometimes useful to be able to + * execute certain set up or tear down code outside of a + * transaction. {@code TransactionalTestExecutionListener} provides such * support for methods annotated with - * {@link BeforeTransaction @BeforeTransaction} and - * {@link AfterTransaction @AfterTransaction}. - *

+ * {@link BeforeTransaction @BeforeTransaction} and + * {@link AfterTransaction @AfterTransaction}. * * @author Sam Brannen * @author Juergen Hoeller @@ -96,25 +96,33 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis private static final Log logger = LogFactory.getLog(TransactionalTestExecutionListener.class); + private static final String DEFAULT_TRANSACTION_MANAGER_NAME = (String) AnnotationUtils.getDefaultValue( + TransactionConfiguration.class, "transactionManager"); + + private static final Boolean DEFAULT_DEFAULT_ROLLBACK = (Boolean) AnnotationUtils.getDefaultValue( + TransactionConfiguration.class, "defaultRollback"); + protected final TransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource(); + private final Map transactionContextCache = + Collections.synchronizedMap(new IdentityHashMap()); + private TransactionConfigurationAttributes configurationAttributes; private volatile int transactionsStarted = 0; - private final Map transactionContextCache = Collections.synchronizedMap(new IdentityHashMap()); - /** * If the test method of the supplied {@link TestContext test context} is * configured to run within a transaction, this method will run * {@link BeforeTransaction @BeforeTransaction methods} and start a new * transaction. - *

Note that if a {@link BeforeTransaction @BeforeTransaction method} fails, - * remaining {@link BeforeTransaction @BeforeTransaction methods} will not + *

Note that if a {@code BeforeTransaction @BeforeTransaction method} fails, + * remaining {@code BeforeTransaction @BeforeTransaction methods} will not * be invoked, and a transaction will not be started. * @see org.springframework.transaction.annotation.Transactional * @see org.springframework.test.annotation.NotTransactional + * @see #getTransactionManager(TestContext, String) */ @SuppressWarnings("serial") @Override @@ -131,36 +139,27 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis return; } + PlatformTransactionManager tm = null; TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod, testContext.getTestClass()); - TransactionDefinition transactionDefinition = null; + if (transactionAttribute != null) { - transactionDefinition = new DelegatingTransactionAttribute(transactionAttribute) { + transactionAttribute = new DelegatingTransactionAttribute(transactionAttribute) { public String getName() { return testMethod.getName(); } }; - } - if (transactionDefinition != null) { if (logger.isDebugEnabled()) { - logger.debug("Explicit transaction definition [" + transactionDefinition + "] found for test context [" - + testContext + "]"); + logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context " + + testContext); } - String qualifier = transactionAttribute.getQualifier(); - PlatformTransactionManager tm; - if (StringUtils.hasLength(qualifier)) { - // Use autowire-capable factory in order to support extended - // qualifier matching (only exposed on the internal BeanFactory, - // not on the ApplicationContext). - BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory(); - tm = BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier); - } - else { - tm = getTransactionManager(testContext); - } - TransactionContext txContext = new TransactionContext(tm, transactionDefinition); + tm = getTransactionManager(testContext, transactionAttribute.getQualifier()); + } + + if (tm != null) { + TransactionContext txContext = new TransactionContext(tm, transactionAttribute); runBeforeTransactionMethods(testContext); startNewTransaction(testContext, txContext); this.transactionContextCache.put(testMethod, txContext); @@ -171,7 +170,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis * If a transaction is currently active for the test method of the supplied * {@link TestContext test context}, this method will end the transaction * and run {@link AfterTransaction @AfterTransaction methods}. - *

{@link AfterTransaction @AfterTransaction methods} are guaranteed to be + *

{@code AfterTransaction @AfterTransaction methods} are guaranteed to be * invoked even if an error occurs while ending the transaction. */ @Override @@ -205,15 +204,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis Collections.reverse(methods); for (Method method : methods) { if (logger.isDebugEnabled()) { - logger.debug("Executing @BeforeTransaction method [" + method + "] for test context [" - + testContext + "]"); + logger.debug("Executing @BeforeTransaction method [" + method + "] for test context " + testContext); } method.invoke(testContext.getTestInstance()); } } catch (InvocationTargetException ex) { - logger.error("Exception encountered while executing @BeforeTransaction methods for test context [" - + testContext + "]", ex.getTargetException()); + logger.error("Exception encountered while executing @BeforeTransaction methods for test context " + + testContext + ".", ex.getTargetException()); ReflectionUtils.rethrowException(ex.getTargetException()); } } @@ -233,8 +231,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis for (Method method : methods) { try { if (logger.isDebugEnabled()) { - logger.debug("Executing @AfterTransaction method [" + method + "] for test context [" + testContext - + "]"); + logger.debug("Executing @AfterTransaction method [" + method + "] for test context " + testContext); } method.invoke(testContext.getTestInstance()); } @@ -244,14 +241,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis afterTransactionException = targetException; } logger.error("Exception encountered while executing @AfterTransaction method [" + method - + "] for test context [" + testContext + "]", targetException); + + "] for test context " + testContext, targetException); } catch (Exception ex) { if (afterTransactionException == null) { afterTransactionException = ex; } logger.error("Exception encountered while executing @AfterTransaction method [" + method - + "] for test context [" + testContext + "]", ex); + + "] for test context " + testContext, ex); } } @@ -287,14 +284,50 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis private void endTransaction(TestContext testContext, TransactionContext txContext) throws Exception { boolean rollback = isRollback(testContext); if (logger.isTraceEnabled()) { - logger.trace("Ending transaction for test context [" + testContext + "]; transaction manager [" + logger.trace("Ending transaction for test context " + testContext + "; transaction manager [" + txContext.transactionStatus + "]; rollback [" + rollback + "]"); } txContext.endTransaction(rollback); if (logger.isInfoEnabled()) { logger.info((rollback ? "Rolled back" : "Committed") - + " transaction after test execution for test context [" + testContext + "]"); + + " transaction after test execution for test context " + testContext); + } + } + + /** + * Get the {@link PlatformTransactionManager transaction manager} to use + * for the supplied {@link TestContext test context} and {@code qualifier}. + *

Delegates to {@link #getTransactionManager(TestContext)} if the + * supplied {@code qualifier} is null or empty. + * @param testContext the test context for which the transaction manager + * should be retrieved + * @param qualifier the qualifier for selecting between multiple bean matches; + * may be null or empty + * @return the transaction manager to use, or null if not found + * @throws BeansException if an error occurs while retrieving the transaction manager + * @see #getTransactionManager(TestContext) + */ + protected final PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) { + // look up by type and qualifier from @Transactional + if (StringUtils.hasText(qualifier)) { + try { + // Use autowire-capable factory in order to support extended qualifier + // matching (only exposed on the internal BeanFactory, not on the + // ApplicationContext). + BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory(); + + return BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier); + } catch (RuntimeException ex) { + if (logger.isWarnEnabled()) { + logger.warn("Caught exception while retrieving transaction manager for test context " + testContext + + " and qualifier [" + qualifier + "]", ex); + } + throw ex; + } } + + // else + return getTransactionManager(testContext); } /** @@ -304,16 +337,34 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis * should be retrieved * @return the transaction manager to use, or null if not found * @throws BeansException if an error occurs while retrieving the transaction manager + * @see #getTransactionManager(TestContext, String) */ protected final PlatformTransactionManager getTransactionManager(TestContext testContext) { + BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory(); String tmName = retrieveConfigurationAttributes(testContext).getTransactionManagerName(); + try { - return testContext.getApplicationContext().getBean(tmName, PlatformTransactionManager.class); - } - catch (BeansException ex) { + // look up by type and explicit name from @TransactionConfiguration + if (StringUtils.hasText(tmName) && !DEFAULT_TRANSACTION_MANAGER_NAME.equals(tmName)) { + return bf.getBean(tmName, PlatformTransactionManager.class); + } + + // look up single bean by type + if (bf instanceof ListableBeanFactory) { + ListableBeanFactory lbf = (ListableBeanFactory) bf; + Map beansOfType = BeanFactoryUtils.beansOfTypeIncludingAncestors( + lbf, PlatformTransactionManager.class); + if (beansOfType.size() == 1) { + return beansOfType.values().iterator().next(); + } + } + + // look up by type and default name from @TransactionConfiguration + return bf.getBean(DEFAULT_TRANSACTION_MANAGER_NAME, PlatformTransactionManager.class); + + } catch (BeansException ex) { if (logger.isWarnEnabled()) { - logger.warn("Caught exception while retrieving transaction manager with bean name [" + tmName - + "] for test context [" + testContext + "]", ex); + logger.warn("Caught exception while retrieving transaction manager for test context " + testContext, ex); } throw ex; } @@ -348,14 +399,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis boolean rollbackOverride = rollbackAnnotation.value(); if (logger.isDebugEnabled()) { logger.debug("Method-level @Rollback(" + rollbackOverride + ") overrides default rollback [" + rollback - + "] for test context [" + testContext + "]"); + + "] for test context " + testContext); } rollback = rollbackOverride; } else { if (logger.isDebugEnabled()) { logger.debug("No method-level @Rollback override: using default rollback [" + rollback - + "] for test context [" + testContext + "]"); + + "] for test context " + testContext); } } return rollback; @@ -451,20 +502,19 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis /** * Retrieves the {@link TransactionConfigurationAttributes} for the - * specified {@link Class class} which may optionally declare or inherit a - * {@link TransactionConfiguration @TransactionConfiguration}. If a - * {@link TransactionConfiguration} annotation is not present for the - * supplied class, the default values for attributes defined in - * {@link TransactionConfiguration} will be used instead. - * @param clazz the Class object corresponding to the test class for which - * the configuration attributes should be retrieved + * specified {@link Class class} which may optionally declare or inherit + * {@link TransactionConfiguration @TransactionConfiguration}. If + * {@code @TransactionConfiguration} is not present for the supplied + * class, the default values for attributes defined in + * {@code @TransactionConfiguration} will be used instead. + * @param testContext the test context for which the configuration + * attributes should be retrieved * @return a new TransactionConfigurationAttributes instance */ private TransactionConfigurationAttributes retrieveConfigurationAttributes(TestContext testContext) { if (this.configurationAttributes == null) { Class clazz = testContext.getTestClass(); - Class annotationType = TransactionConfiguration.class; - TransactionConfiguration config = clazz.getAnnotation(annotationType); + TransactionConfiguration config = clazz.getAnnotation(TransactionConfiguration.class); if (logger.isDebugEnabled()) { logger.debug("Retrieved @TransactionConfiguration [" + config + "] for test class [" + clazz + "]"); } @@ -476,14 +526,14 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis defaultRollback = config.defaultRollback(); } else { - transactionManagerName = (String) AnnotationUtils.getDefaultValue(annotationType, "transactionManager"); - defaultRollback = (Boolean) AnnotationUtils.getDefaultValue(annotationType, "defaultRollback"); + transactionManagerName = DEFAULT_TRANSACTION_MANAGER_NAME; + defaultRollback = DEFAULT_DEFAULT_ROLLBACK; } TransactionConfigurationAttributes configAttributes = new TransactionConfigurationAttributes( transactionManagerName, defaultRollback); if (logger.isDebugEnabled()) { - logger.debug("Retrieved TransactionConfigurationAttributes [" + configAttributes + "] for class [" + logger.debug("Retrieved TransactionConfigurationAttributes " + configAttributes + " for class [" + clazz + "]"); } this.configurationAttributes = configAttributes; 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 new file mode 100644 index 0000000000000000000000000000000000000000..0a70b0d0a25c6fd2acfeb788373d622992178abb --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpNonexistentTxMgrTests.java @@ -0,0 +1,77 @@ +/* + * 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.spr9645; + +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; + +/** + * Integration tests that verify the behavior requested in + * SPR-9645. + * + * @author Sam Brannen + * @since 3.2 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class LookUpNonexistentTxMgrTests { + + private static final CallCountingTransactionManager txManager = new CallCountingTransactionManager(); + + + @Configuration + static class Config { + + @Bean + public PlatformTransactionManager transactionManager() { + return txManager; + } + } + + + @BeforeTransaction + public void beforeTransaction() { + txManager.clear(); + } + + @Test + public void lookUpNothing() { + 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 new file mode 100644 index 0000000000000000000000000000000000000000..5cfdebaba386e7ed1d548f27c5bb70131e73d5e2 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndDefaultNameTests.java @@ -0,0 +1,84 @@ +/* + * 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.spr9645; + +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.Transactional; + +/** + * Integration tests that verify the behavior requested in + * SPR-9645. + * + * @author Sam Brannen + * @since 3.2 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@Transactional +public class LookUpTxMgrByTypeAndDefaultNameTests { + + private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager(); + private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager(); + + @Configuration + static class Config { + + @Bean + public PlatformTransactionManager transactionManager() { + return txManager1; + } + + @Bean + public PlatformTransactionManager txManager2() { + return txManager2; + } + } + + @BeforeTransaction + public void beforeTransaction() { + txManager1.clear(); + txManager2.clear(); + } + + @Test + public void lookUpByTypeAndDefaultName() { + assertEquals(1, txManager1.begun); + assertEquals(1, txManager1.inflight); + assertEquals(0, txManager1.commits); + assertEquals(0, txManager1.rollbacks); + } + + @AfterTransaction + public void afterTransaction() { + assertEquals(1, txManager1.begun); + assertEquals(0, txManager1.inflight); + assertEquals(0, txManager1.commits); + assertEquals(1, txManager1.rollbacks); + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..805243f11c96cd3f8817a3807c3c57084216913e --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndNameTests.java @@ -0,0 +1,86 @@ +/* + * 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.spr9645; + +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.context.transaction.TransactionConfiguration; +import org.springframework.test.transaction.CallCountingTransactionManager; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests that verify the behavior requested in + * SPR-9645. + * + * @author Sam Brannen + * @since 3.2 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@Transactional +@TransactionConfiguration(transactionManager = "txManager1") +public class LookUpTxMgrByTypeAndNameTests { + + private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager(); + private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager(); + + @Configuration + static class Config { + + @Bean + public PlatformTransactionManager txManager1() { + return txManager1; + } + + @Bean + public PlatformTransactionManager txManager2() { + return txManager2; + } + } + + @BeforeTransaction + public void beforeTransaction() { + txManager1.clear(); + txManager2.clear(); + } + + @Test + public void lookUpByTypeAndName() { + assertEquals(1, txManager1.begun); + assertEquals(1, txManager1.inflight); + assertEquals(0, txManager1.commits); + assertEquals(0, txManager1.rollbacks); + } + + @AfterTransaction + public void afterTransaction() { + assertEquals(1, txManager1.begun); + assertEquals(0, txManager1.inflight); + assertEquals(0, txManager1.commits); + assertEquals(1, txManager1.rollbacks); + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..cd78baea45d886369acfa31b9c7b30c07dc5b692 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtClassLevelTests.java @@ -0,0 +1,84 @@ +/* + * 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.spr9645; + +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.Transactional; + +/** + * Integration tests that verify the behavior requested in + * SPR-9645. + * + * @author Sam Brannen + * @since 3.2 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@Transactional("txManager1") +public class LookUpTxMgrByTypeAndQualifierAtClassLevelTests { + + private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager(); + private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager(); + + @Configuration + static class Config { + + @Bean + public PlatformTransactionManager txManager1() { + return txManager1; + } + + @Bean + public PlatformTransactionManager txManager2() { + return txManager2; + } + } + + @BeforeTransaction + public void beforeTransaction() { + txManager1.clear(); + txManager2.clear(); + } + + @Test + public void lookUpByTypeAndQualifier() { + assertEquals(1, txManager1.begun); + assertEquals(1, txManager1.inflight); + assertEquals(0, txManager1.commits); + assertEquals(0, txManager1.rollbacks); + } + + @AfterTransaction + public void afterTransaction() { + assertEquals(1, txManager1.begun); + assertEquals(0, txManager1.inflight); + assertEquals(0, txManager1.commits); + assertEquals(1, txManager1.rollbacks); + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..0a9335dbcaad1b39d0b8942b1f585b2afa1a1170 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeAndQualifierAtMethodLevelTests.java @@ -0,0 +1,84 @@ +/* + * 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.spr9645; + +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.Transactional; + +/** + * Integration tests that verify the behavior requested in + * SPR-9645. + * + * @author Sam Brannen + * @since 3.2 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class LookUpTxMgrByTypeAndQualifierAtMethodLevelTests { + + private static final CallCountingTransactionManager txManager1 = new CallCountingTransactionManager(); + private static final CallCountingTransactionManager txManager2 = new CallCountingTransactionManager(); + + @Configuration + static class Config { + + @Bean + public PlatformTransactionManager txManager1() { + return txManager1; + } + + @Bean + public PlatformTransactionManager txManager2() { + return txManager2; + } + } + + @BeforeTransaction + public void beforeTransaction() { + txManager1.clear(); + txManager2.clear(); + } + + @Transactional("txManager1") + @Test + public void lookUpByTypeAndQualifier() { + assertEquals(1, txManager1.begun); + assertEquals(1, txManager1.inflight); + assertEquals(0, txManager1.commits); + assertEquals(0, txManager1.rollbacks); + } + + @AfterTransaction + public void afterTransaction() { + assertEquals(1, txManager1.begun); + assertEquals(0, txManager1.inflight); + assertEquals(0, txManager1.commits); + assertEquals(1, txManager1.rollbacks); + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..ed4847980585c2394ee6ea2b349d0d34482c4476 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9645/LookUpTxMgrByTypeTests.java @@ -0,0 +1,77 @@ +/* + * 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.spr9645; + +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.Transactional; + +/** + * Integration tests that verify the behavior requested in + * SPR-9645. + * + * @author Sam Brannen + * @since 3.2 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@Transactional +public class LookUpTxMgrByTypeTests { + + private static final CallCountingTransactionManager txManager = new CallCountingTransactionManager(); + + @Configuration + static class Config { + + @Bean + public PlatformTransactionManager txManager() { + return txManager; + } + } + + @BeforeTransaction + public void beforeTransaction() { + txManager.clear(); + } + + @Test + public void lookUpByType() { + assertEquals(1, txManager.begun); + assertEquals(1, txManager.inflight); + assertEquals(0, txManager.commits); + assertEquals(0, txManager.rollbacks); + } + + @AfterTransaction + public void afterTransaction() { + assertEquals(1, txManager.begun); + assertEquals(0, txManager.inflight); + assertEquals(0, txManager.commits); + assertEquals(1, txManager.rollbacks); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/transaction/CallCountingTransactionManager.java b/spring-test/src/test/java/org/springframework/test/transaction/CallCountingTransactionManager.java new file mode 100644 index 0000000000000000000000000000000000000000..8713dbe5ee652f275aa8baf59f57d954cb904769 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/transaction/CallCountingTransactionManager.java @@ -0,0 +1,61 @@ +/* + * 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.transaction; + +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.AbstractPlatformTransactionManager; +import org.springframework.transaction.support.DefaultTransactionStatus; + +/** + * @author Rod Johnson + * @author Juergen Hoeller + */ +@SuppressWarnings("serial") +public class CallCountingTransactionManager extends AbstractPlatformTransactionManager { + + public TransactionDefinition lastDefinition; + public int begun; + public int commits; + public int rollbacks; + public int inflight; + + + protected Object doGetTransaction() { + return new Object(); + } + + protected void doBegin(Object transaction, TransactionDefinition definition) { + this.lastDefinition = definition; + ++begun; + ++inflight; + } + + protected void doCommit(DefaultTransactionStatus status) { + ++commits; + --inflight; + } + + protected void doRollback(DefaultTransactionStatus status) { + ++rollbacks; + --inflight; + } + + public void clear() { + begun = commits = rollbacks = inflight = 0; + } + +} diff --git a/spring-test/src/test/resources/log4j.xml b/spring-test/src/test/resources/log4j.xml index bcfcb8f81af19ad59abe04dec0d6b5e691d482e0..873d2ccdc45707d54c5566dbf1136df2c1eb3d45 100644 --- a/spring-test/src/test/resources/log4j.xml +++ b/spring-test/src/test/resources/log4j.xml @@ -41,6 +41,10 @@ --> + + + + diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index d5406a535f676f365219e6f3132899465f61bea6..4c8f1c6443458b13529f029f8411226efbe46dce 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -28,6 +28,7 @@ Changes in version 3.2 M2 (2012-08-xx) * add exclude patterns for mapped interceptors in MVC namespace and MVC Java config * 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) Changes in version 3.2 M1 (2012-05-28) diff --git a/src/reference/docbook/testing.xml b/src/reference/docbook/testing.xml index 1af3b663f4b966958731d49729d079281f206b00..0f8a4bf064aa0e4fb0fdd1b502fa47b266e2cf1d 100644 --- a/src/reference/docbook/testing.xml +++ b/src/reference/docbook/testing.xml @@ -8,7 +8,7 @@ xmlns:ns="http://docbook.org/ns/docbook"> Testing -

+
Introduction to Spring Testing Testing is an integral part of enterprise software development. This @@ -20,7 +20,7 @@ this reference manual.)
-
+
Unit Testing Dependency Injection should make your code less dependent on the @@ -44,10 +44,10 @@ however, the Spring Framework provides the following mock objects and testing support classes. -
+
Mock Objects -
+
JNDI The org.springframework.mock.jndi package @@ -59,20 +59,20 @@ testing scenarios without modification.
-
+
Servlet API The org.springframework.mock.web package contains a comprehensive set of Servlet API mock objects, targeted at usage with Spring's Web MVC framework, which are useful for testing web contexts and controllers. These mock objects are generally more - convenient to use than dynamic mock objects such as EasyMock or existing Servlet API - mock objects such as MockObjects. + convenient to use than dynamic mock objects such as EasyMock or existing + Servlet API mock objects such as MockObjects.
-
+
Portlet API The org.springframework.mock.web.portlet @@ -81,10 +81,10 @@
-
+
Unit Testing support Classes -
+
General utilities The org.springframework.test.util package @@ -115,7 +115,7 @@
-
+
Spring MVC The org.springframework.test.web package @@ -131,18 +131,18 @@ ModelAndViewAssert combined with MockHttpServletRequest, MockHttpSession, and so on from the - org.springframework.mock.web - package. + linkend="mock-objects-servlet"> + org.springframework.mock.web + package.
-
+
Integration Testing -
+
Overview It is important to be able to perform some integration testing @@ -211,7 +211,7 @@
-
+
Goals of Integration Testing Spring's integration testing support has the following primary @@ -244,7 +244,7 @@ The next few sections describe each goal and provide links to implementation and configuration details. -
+
Context management and caching The Spring TestContext Framework provides consistent loading of @@ -284,7 +284,7 @@ framework.
-
+
Dependency Injection of test fixtures When the TestContext framework loads your application context, @@ -328,7 +328,7 @@ linkend="testcontext-fixture-di">TestContext framework.
-
+
Transaction management One common issue in tests that access a real database is their @@ -354,17 +354,16 @@ useful when you want a particular test to populate or modify the database — the TestContext framework can be instructed to cause the transaction to commit instead of roll back via the - @TransactionConfiguration - and - @Rollback - annotations. + linkend="integration-testing-annotations"> + @TransactionConfiguration and + + @Rollback annotations. See transaction management with the TestContext framework.
-
+
Support classes for integration testing The Spring TestContext Framework provides several @@ -403,18 +402,18 @@
-
+
JDBC Testing Support The org.springframework.test.jdbc package contains SimpleJdbcTestUtils, which is a collection of JDBC related utility functions intended to simplify standard database testing scenarios. Note that - AbstractTransactionalJUnit4SpringContextTests - and - AbstractTransactionalTestNGSpringContextTests - provide convenience methods which delegate to + linkend="testcontext-support-classes-junit4"> + AbstractTransactionalJUnit4SpringContextTests + and + AbstractTransactionalTestNGSpringContextTests + provide convenience methods which delegate to SimpleJdbcTestUtils internally. The spring-jdbc module provides support for @@ -424,10 +423,10 @@ linkend="jdbc-embedded-database-dao-testing" />.
-
+
Annotations -
+
Spring Testing Annotations The Spring Framework provides the following set of @@ -439,11 +438,9 @@ - - - @ContextConfiguration - - + + @ContextConfiguration + Defines class-level metadata that is used to determine how to load and configure an @@ -501,11 +498,8 @@ public class CustomLoaderXmlApplicationContextTests { - - - @ActiveProfiles - - + + @ActiveProfiles A class-level annotation that is used to declare which bean definition profiles should be active @@ -538,11 +532,8 @@ public class DeveloperIntegrationTests { - - - @DirtiesContext - - + + @DirtiesContext Indicates that the underlying Spring ApplicationContext has been @@ -610,11 +601,9 @@ public void testProcessWhichDirtiesAppCtx() { - - - @TestExecutionListeners - - + + @TestExecutionListeners + Defines class-level metadata for configuring which TestExecutionListeners should be @@ -635,17 +624,18 @@ public class CustomTestExecutionListenerTests { - - - @TransactionConfiguration - - + + @TransactionConfiguration + 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 configured if - the bean name of the desired + to 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 PlatformTransactionManager is not "transactionManager". In addition, you can change the defaultRollback flag to @@ -666,20 +656,17 @@ public class CustomConfiguredTransactionalTests { If the default conventions are sufficient for your test configuration, you can avoid using @TransactionConfiguration - altogether. In other words, if 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 + 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 @TransactionConfiguration. - - - @Rollback - - + + @Rollback Indicates whether the transaction for the annotated test method should be rolled back after the test @@ -696,11 +683,9 @@ public void testProcessWithoutRollback() { - - - @BeforeTransaction - - + + @BeforeTransaction + Indicates that the annotated public void method should be executed before a @@ -715,11 +700,9 @@ public void testProcessWithoutRollback() { - - - @AfterTransaction - - + + @AfterTransaction + Indicates that the annotated public void method should be executed after a transaction @@ -734,11 +717,9 @@ public void testProcessWithoutRollback() { - - - @NotTransactional - - + + @NotTransactional + The presence of this annotation indicates that the annotated test method must not execute in a @@ -771,7 +752,7 @@ public void testProcessWithoutTransaction() {
-
+
Standard Annotation Support The following annotations are supported with standard semantics @@ -781,70 +762,54 @@ public void testProcessWithoutTransaction() { - - - @Autowired - - + + @Autowired - - - @Qualifier - - + + @Qualifier - @Resource - (javax.annotation) if JSR-250 is + @Resource + (javax.annotation) if JSR-250 is present - @Inject - (javax.inject) if JSR-330 is - present + @Inject (javax.inject) + if JSR-330 is present - - @Named - (javax.inject) if JSR-330 is + @Named + (javax.inject) if JSR-330 is present - @PersistenceContext - (javax.persistence) if JPA is - present + @PersistenceContext + (javax.persistence) if JPA is present - @PersistenceUnit - (javax.persistence) if JPA is - present + @PersistenceUnit + (javax.persistence) if JPA is present - - - @Required - - + + @Required - - - @Transactional - - + + @Transactional @@ -875,7 +840,7 @@ public void testProcessWithoutTransaction() {
-
+
Spring JUnit Testing Annotations The following annotations are only @@ -886,11 +851,8 @@ public void testProcessWithoutTransaction() { - - - @IfProfileValue - - + + @IfProfileValue Indicates that the annotated test is enabled for a specific testing environment. If the configured @@ -925,11 +887,9 @@ public void testProcessWhichRunsForUnitOrIntegrationTestGroups() { - - - @ProfileValueSourceConfiguration - - + + @ProfileValueSourceConfiguration + Class-level annotation that specifies what type of ProfileValueSource to use when retrieving @@ -947,11 +907,8 @@ public class CustomProfileValueSourceTests { - - - @Timed - - + @Timed + Indicates that the annotated test method must finish execution in a specified time period (in milliseconds). If the @@ -985,11 +942,8 @@ public void testProcessWithOneSecondTimeout() { - - - @Repeat - - + + @Repeat Indicates that the annotated test method must be executed repeatedly. The number of times that the test method is to be @@ -1009,7 +963,7 @@ public void testProcessRepeatedly() {
-
+
Spring TestContext Framework The Spring TestContext @@ -1041,7 +995,7 @@ public void testProcessRepeatedly() { linkend="integration-testing-annotations">annotation support sections. -
+
Key abstractions The core of the framework consists of the @@ -1202,7 +1156,7 @@ public void testProcessRepeatedly() { with the framework.
-
+
Context management Each TestContext provides context @@ -1264,7 +1218,7 @@ public class MyTest { @ContextConfiguration annotation. -
+
Context configuration with XML resources To load an ApplicationContext @@ -1323,14 +1277,14 @@ public class MyTest { package com.example; @RunWith(SpringJUnit4ClassRunner.class) -// ApplicationContext will be loaded from "classpath:/com/example/MyTest-context.xml" +// ApplicationContext will be loaded from "classpath:/com/example/MyTest-context.xml" @ContextConfiguration public class MyTest { // class body... }
-
+
Context configuration with annotated classes To load an ApplicationContext @@ -1397,7 +1351,7 @@ public class OrderServiceTest { }
-
+
Mixing XML resources and annotated classes It may sometimes be desirable to mix XML resources and @@ -1433,7 +1387,7 @@ public class OrderServiceTest { other type of configuration.
-
+
Context configuration inheritance @ContextConfiguration supports @@ -1466,13 +1420,13 @@ public class OrderServiceTest { "base-config.xml". @RunWith(SpringJUnit4ClassRunner.class) -// ApplicationContext will be loaded from "/base-config.xml" in the root of the classpath +// ApplicationContext will be loaded from "/base-config.xml" in the root of the classpath @ContextConfiguration("/base-config.xml") public class BaseTest { // class body... } -// ApplicationContext will be loaded from "/base-config.xml" and "/extended-config.xml" +// ApplicationContext will be loaded from "/base-config.xml" and "/extended-config.xml" // in the root of the classpath @ContextConfiguration("/extended-config.xml") public class ExtendedTest extends BaseTest { @@ -1502,7 +1456,7 @@ public class ExtendedTest extends BaseTest { }
-
+
Context configuration with environment profiles Spring 3.1 introduces first-class support in the framework for @@ -1711,7 +1665,7 @@ public class TransferServiceTest {
-
+
Context caching Once the TestContext framework loads an @@ -1733,35 +1687,23 @@ public class TransferServiceTest { - - locations - - (from @ContextConfiguration) - + locations (from + @ContextConfiguration) - - classes - - (from @ContextConfiguration) - + classes (from + @ContextConfiguration) - - contextLoader - - (from @ContextConfiguration) - + contextLoader (from + @ContextConfiguration) - - activeProfiles - - (from @ActiveProfiles) - + activeProfiles (from + @ActiveProfiles) @@ -1799,8 +1741,8 @@ public class TransferServiceTest { executing tests with a build framework such as Ant, Maven, or Gradle it is important to make sure that the build framework does not fork between tests. For example, if the - forkMode + forkMode for the Maven Surefire plug-in is set to always or pertest, the TestContext framework will not be able to cache application contexts between test classes and the @@ -1823,7 +1765,7 @@ public class TransferServiceTest {
-
+
Dependency injection of test fixtures When you use the @@ -1848,13 +1790,13 @@ public class TransferServiceTest { Because @Autowired is used to - perform - autowiring by type - , if you have multiple bean definitions of the same type, you - cannot rely on this approach for those particular beans. In that case, - you can use @Autowired in conjunction - with @Qualifier. As of Spring 3.0 you - may also choose to use @Inject in + perform autowiring + by type , if you have multiple bean definitions of + the same type, you cannot rely on this approach for those particular + beans. In that case, you can use + @Autowired in conjunction with + @Qualifier. As of Spring 3.0 you may + also choose to use @Inject in conjunction with @Named. Alternatively, if your test class has access to its ApplicationContext, you can perform an explicit @@ -1901,8 +1843,7 @@ public class TransferServiceTest { @ContextConfiguration("repository-config.xml") public class HibernateTitleRepositoryTests { - // this instance will be dependency injected by type + // this instance will be dependency injected by type @Autowired private HibernateTitleRepository titleRepository; @@ -1922,8 +1863,7 @@ public class HibernateTitleRepositoryTests { @ContextConfiguration("repository-config.xml") public class HibernateTitleRepositoryTests { - // this instance will be dependency injected by type + // this instance will be dependency injected by type private HibernateTitleRepository titleRepository; @Autowired @@ -1949,7 +1889,7 @@ public class HibernateTitleRepositoryTests { xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - <!-- this bean will be injected into the HibernateTitleRepositoryTests class --> + <!-- this bean will be injected into the HibernateTitleRepositoryTests class --> <bean id="titleRepository" class="com.foo.repository.hibernate.HibernateTitleRepository"> <property name="sessionFactory" ref="sessionFactory"/> @@ -1994,7 +1934,7 @@ public class HibernateTitleRepositoryTests {
-
+
Transaction management In the TestContext framework, transactions are managed by the @@ -2010,10 +1950,11 @@ public class HibernateTitleRepositoryTests { @Transactional either at the class or method level for your tests. - For class-level transaction configuration (i.e., setting the - bean name for the transaction manager and the default rollback flag), - see the @TransactionConfiguration entry - in the annotation + For class-level transaction configuration (i.e., setting an + explicit bean name for the transaction manager and the default + rollback flag), see the + @TransactionConfiguration entry in the + annotation support section. If transactions are not enabled for the entire test class, you @@ -2023,14 +1964,12 @@ public class HibernateTitleRepositoryTests { the @Rollback annotation to override the class-level default rollback setting. - - - AbstractTransactionalJUnit4SpringContextTests - and - AbstractTransactionalTestNGSpringContextTests - are preconfigured for transactional support at the class - level. - + + AbstractTransactionalJUnit4SpringContextTests + and + AbstractTransactionalTestNGSpringContextTests + are preconfigured for transactional support at the class + level. Occasionally you need to execute certain code before or after a transactional test method but outside the transactional context, for @@ -2104,7 +2043,7 @@ public class FictitiousTransactionalTest { } - + Avoid false positives when testing ORM code @@ -2144,10 +2083,10 @@ public void updateWithSessionFlush() {
-
+
TestContext support classes -
+
JUnit support classes The org.springframework.test.context.junit4 @@ -2227,7 +2166,7 @@ public void updateWithSessionFlush() {
-
+
Spring JUnit Runner The Spring TestContext Framework offers @@ -2257,7 +2196,7 @@ public class SimpleTest { }
-
+
TestNG support classes The org.springframework.test.context.testng @@ -2339,7 +2278,7 @@ public class SimpleTest {
-
+
PetClinic Example The PetClinic application, available from the
-
+
Further Resources Consult the following resources for more information about @@ -2484,62 +2423,61 @@ public class HibernateClinicTests extends AbstractClinicTests { } - JUnit: - A programmer-oriented testing framework for - Java - . Used by the Spring Framework in its test suite. + JUnit: + A programmer-oriented testing framework for Java + . Used by the Spring Framework in its test suite. - TestNG: A testing + TestNG: A testing framework inspired by JUnit with added support for Java 5 annotations, test groups, data-driven testing, distributed testing, etc. - MockObjects.com: Web site - dedicated to mock objects, a technique for improving the design of - code within test-driven development. + MockObjects.com: Web + site dedicated to mock objects, a technique for improving the design + of code within test-driven development. - "Mock - Objects": Article in Wikipedia. + "Mock + Objects": Article in Wikipedia. - EasyMock: Java - library - that provides Mock Objects for interfaces (and objects - through the class extension) by generating them on the fly using - Java's proxy mechanism. - Used by the Spring Framework in its test suite. + EasyMock: + Java library that provides Mock Objects for + interfaces (and objects through the class extension) by generating + them on the fly using Java's proxy mechanism. Used + by the Spring Framework in its test suite. - JMock: Library that - supports test-driven development of Java code with mock + JMock: Library + that supports test-driven development of Java code with mock objects. - Mockito: Java mock - library based on the test spy + Mockito: Java mock + library based on the test spy pattern. - DbUnit: + DbUnit: JUnit extension (also usable with Ant and Maven) targeted for database-driven projects that, among other things, puts your database into a known state between test runs. - The - Grinder: Java load testing framework. + The + Grinder: Java load testing framework.