Browse Source

Concurrency and exception message refinements for test transactions

pull/1717/merge
Juergen Hoeller 7 years ago
parent
commit
a0cc80063d
  1. 8
      spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java
  2. 8
      spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java
  3. 61
      spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java
  4. 19
      spring-test/src/main/java/org/springframework/test/context/transaction/TestTransaction.java
  5. 24
      spring-test/src/main/java/org/springframework/test/context/transaction/TransactionContext.java
  6. 6
      spring-test/src/main/java/org/springframework/test/context/transaction/TransactionContextHolder.java
  7. 20
      spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
  8. 76
      spring-test/src/test/java/org/springframework/test/context/transaction/PrimaryTransactionManagerTests.java
  9. 190
      spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java

8
spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@ -28,15 +28,15 @@ import java.lang.annotation.Target; @@ -28,15 +28,15 @@ import java.lang.annotation.Target;
* configured to run within a transaction via Spring's {@code @Transactional}
* annotation.
*
* <p>As of Spring Framework 4.3, {@code @AfterTransaction} may be declared on
* Java 8 based interface default methods.
*
* <p>{@code @AfterTransaction} methods declared in superclasses or as interface
* default methods will be executed after those of the current test class.
*
* <p>As of Spring Framework 4.0, this annotation may be used as a
* <em>meta-annotation</em> to create custom <em>composed annotations</em>.
*
* <p>As of Spring Framework 4.3, {@code @AfterTransaction} may also be
* declared on Java 8 based interface default methods.
*
* @author Sam Brannen
* @since 2.5
* @see org.springframework.transaction.annotation.Transactional

8
spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@ -28,15 +28,15 @@ import java.lang.annotation.Target; @@ -28,15 +28,15 @@ import java.lang.annotation.Target;
* configured to run within a transaction via Spring's {@code @Transactional}
* annotation.
*
* <p>As of Spring Framework 4.3, {@code @BeforeTransaction} may be declared on
* Java 8 based interface default methods.
*
* <p>{@code @BeforeTransaction} methods declared in superclasses or as interface
* default methods will be executed before those of the current test class.
*
* <p>As of Spring Framework 4.0, this annotation may be used as a
* <em>meta-annotation</em> to create custom <em>composed annotations</em>.
*
* <p>As of Spring Framework 4.3, {@code @BeforeTransaction} may also be
* declared on Java 8 based interface default methods.
*
* @author Sam Brannen
* @since 2.5
* @see org.springframework.transaction.annotation.Transactional

61
spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
package org.springframework.test.context.transaction;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
@ -40,6 +39,7 @@ import org.springframework.util.StringUtils; @@ -40,6 +39,7 @@ import org.springframework.util.StringUtils;
/**
* Utility methods for working with transactions and data access related beans
* within the <em>Spring TestContext Framework</em>.
*
* <p>Mainly for internal use within the framework.
*
* @author Sam Brannen
@ -48,8 +48,6 @@ import org.springframework.util.StringUtils; @@ -48,8 +48,6 @@ import org.springframework.util.StringUtils;
*/
public abstract class TestContextTransactionUtils {
private static final Log logger = LogFactory.getLog(TestContextTransactionUtils.class);
/**
* Default bean name for a {@link DataSource}: {@code "dataSource"}.
*/
@ -62,9 +60,8 @@ public abstract class TestContextTransactionUtils { @@ -62,9 +60,8 @@ public abstract class TestContextTransactionUtils {
public static final String DEFAULT_TRANSACTION_MANAGER_NAME = "transactionManager";
private TestContextTransactionUtils() {
/* prevent instantiation */
}
private static final Log logger = LogFactory.getLog(TestContextTransactionUtils.class);
/**
* Retrieve the {@link DataSource} to use for the supplied {@linkplain TestContext
@ -82,8 +79,8 @@ public abstract class TestContextTransactionUtils { @@ -82,8 +79,8 @@ public abstract class TestContextTransactionUtils {
* {@linkplain #DEFAULT_DATA_SOURCE_NAME default data source name}.
* @param testContext the test context for which the {@code DataSource}
* should be retrieved; never {@code null}
* @param name the name of the {@code DataSource} to retrieve; may be {@code null}
* or <em>empty</em>
* @param name the name of the {@code DataSource} to retrieve
* (may be {@code null} or <em>empty</em>)
* @return the {@code DataSource} to use, or {@code null} if not found
* @throws BeansException if an error occurs while retrieving an explicitly
* named {@code DataSource}
@ -94,14 +91,14 @@ public abstract class TestContextTransactionUtils { @@ -94,14 +91,14 @@ public abstract class TestContextTransactionUtils {
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
try {
// look up by type and explicit name
// Look up by type and explicit name
if (StringUtils.hasText(name)) {
return bf.getBean(name, DataSource.class);
}
}
catch (BeansException ex) {
logger.error(
String.format("Failed to retrieve DataSource named '%s' for test context %s", name, testContext), ex);
logger.error(String.format("Failed to retrieve DataSource named '%s' for test context %s",
name, testContext), ex);
throw ex;
}
@ -109,9 +106,9 @@ public abstract class TestContextTransactionUtils { @@ -109,9 +106,9 @@ public abstract class TestContextTransactionUtils {
if (bf instanceof ListableBeanFactory) {
ListableBeanFactory lbf = (ListableBeanFactory) bf;
// look up single bean by type
Map<String, DataSource> dataSources = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf,
DataSource.class);
// Look up single bean by type
Map<String, DataSource> dataSources =
BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, DataSource.class);
if (dataSources.size() == 1) {
return dataSources.values().iterator().next();
}
@ -153,8 +150,8 @@ public abstract class TestContextTransactionUtils { @@ -153,8 +150,8 @@ public abstract class TestContextTransactionUtils {
* name}.
* @param testContext the test context for which the transaction manager
* should be retrieved; never {@code null}
* @param name the name of the transaction manager to retrieve; may be
* {@code null} or <em>empty</em>
* @param name the name of the transaction manager to retrieve
* (may be {@code null} or <em>empty</em>)
* @return the transaction manager to use, or {@code null} if not found
* @throws BeansException if an error occurs while retrieving an explicitly
* named transaction manager
@ -167,14 +164,14 @@ public abstract class TestContextTransactionUtils { @@ -167,14 +164,14 @@ public abstract class TestContextTransactionUtils {
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
try {
// look up by type and explicit name
// Look up by type and explicit name
if (StringUtils.hasText(name)) {
return bf.getBean(name, PlatformTransactionManager.class);
}
}
catch (BeansException ex) {
logger.error(String.format("Failed to retrieve transaction manager named '%s' for test context %s", name,
testContext), ex);
logger.error(String.format("Failed to retrieve transaction manager named '%s' for test context %s",
name, testContext), ex);
throw ex;
}
@ -182,24 +179,24 @@ public abstract class TestContextTransactionUtils { @@ -182,24 +179,24 @@ public abstract class TestContextTransactionUtils {
if (bf instanceof ListableBeanFactory) {
ListableBeanFactory lbf = (ListableBeanFactory) bf;
// look up single bean by type
Map<String, PlatformTransactionManager> txMgrs = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf,
PlatformTransactionManager.class);
// Look up single bean by type
Map<String, PlatformTransactionManager> txMgrs =
BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, PlatformTransactionManager.class);
if (txMgrs.size() == 1) {
return txMgrs.values().iterator().next();
}
try {
// look up single bean by type, with support for 'primary' beans
// Look up single bean by type, with support for 'primary' beans
return bf.getBean(PlatformTransactionManager.class);
}
catch (BeansException ex) {
logBeansException(testContext, ex, PlatformTransactionManager.class);
}
// look up single TransactionManagementConfigurer
Map<String, TransactionManagementConfigurer> configurers = BeanFactoryUtils.beansOfTypeIncludingAncestors(
lbf, TransactionManagementConfigurer.class);
// Look up single TransactionManagementConfigurer
Map<String, TransactionManagementConfigurer> configurers =
BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, TransactionManagementConfigurer.class);
Assert.state(configurers.size() <= 1,
"Only one TransactionManagementConfigurer may exist in the ApplicationContext");
if (configurers.size() == 1) {
@ -227,13 +224,13 @@ public abstract class TestContextTransactionUtils { @@ -227,13 +224,13 @@ public abstract class TestContextTransactionUtils {
* Create a delegating {@link TransactionAttribute} for the supplied target
* {@link TransactionAttribute} and {@link TestContext}, using the names of
* the test class and test method to build the name of the transaction.
*
* @param testContext the {@code TestContext} upon which to base the name; never {@code null}
* @param targetAttribute the {@code TransactionAttribute} to delegate to; never {@code null}
* @param testContext the {@code TestContext} upon which to base the name
* @param targetAttribute the {@code TransactionAttribute} to delegate to
* @return the delegating {@code TransactionAttribute}
*/
public static TransactionAttribute createDelegatingTransactionAttribute(TestContext testContext,
TransactionAttribute targetAttribute) {
public static TransactionAttribute createDelegatingTransactionAttribute(
TestContext testContext, TransactionAttribute targetAttribute) {
Assert.notNull(testContext, "TestContext must not be null");
Assert.notNull(targetAttribute, "Target TransactionAttribute must not be null");
return new TestContextTransactionAttribute(targetAttribute, testContext);

19
spring-test/src/main/java/org/springframework/test/context/transaction/TestTransaction.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@ -50,10 +50,8 @@ public class TestTransaction { @@ -50,10 +50,8 @@ public class TestTransaction {
TransactionContext transactionContext = TransactionContextHolder.getCurrentTransactionContext();
if (transactionContext != null) {
TransactionStatus transactionStatus = transactionContext.getTransactionStatus();
return (transactionStatus != null) && (!transactionStatus.isCompleted());
return (transactionStatus != null && !transactionStatus.isCompleted());
}
// else
return false;
}
@ -80,8 +78,7 @@ public class TestTransaction { @@ -80,8 +78,7 @@ public class TestTransaction {
* Rather, the value of this flag will be used to determine whether or not
* the current test-managed transaction should be rolled back or committed
* once it is {@linkplain #end ended}.
* @throws IllegalStateException if a transaction is not active for the
* current test
* @throws IllegalStateException if no transaction is active for the current test
* @see #isActive()
* @see #isFlaggedForRollback()
* @see #start()
@ -97,8 +94,7 @@ public class TestTransaction { @@ -97,8 +94,7 @@ public class TestTransaction {
* Rather, the value of this flag will be used to determine whether or not
* the current test-managed transaction should be rolled back or committed
* once it is {@linkplain #end ended}.
* @throws IllegalStateException if a transaction is not active for the
* current test
* @throws IllegalStateException if no transaction is active for the current test
* @see #isActive()
* @see #isFlaggedForRollback()
* @see #start()
@ -122,9 +118,9 @@ public class TestTransaction { @@ -122,9 +118,9 @@ public class TestTransaction {
}
/**
* Immediately force a <em>commit</em> or <em>rollback</em> of the current
* test-managed transaction, according to the {@linkplain #isFlaggedForRollback
* rollback flag}.
* Immediately force a <em>commit</em> or <em>rollback</em> of the
* current test-managed transaction, according to the
* {@linkplain #isFlaggedForRollback rollback flag}.
* @throws IllegalStateException if the transaction context could not be
* retrieved or if a transaction is not active for the current test
* @see #isActive()
@ -134,6 +130,7 @@ public class TestTransaction { @@ -134,6 +130,7 @@ public class TestTransaction {
requireCurrentTransactionContext().endTransaction();
}
private static TransactionContext requireCurrentTransactionContext() {
TransactionContext txContext = TransactionContextHolder.getCurrentTransactionContext();
Assert.state(txContext != null, "TransactionContext is not active");

24
spring-test/src/main/java/org/springframework/test/context/transaction/TransactionContext.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
package org.springframework.test.context.transaction;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -53,7 +55,7 @@ class TransactionContext { @@ -53,7 +55,7 @@ class TransactionContext {
@Nullable
private TransactionStatus transactionStatus;
private volatile int transactionsStarted = 0;
private final AtomicInteger transactionsStarted = new AtomicInteger(0);
TransactionContext(TestContext testContext, PlatformTransactionManager transactionManager,
@ -82,8 +84,8 @@ class TransactionContext { @@ -82,8 +84,8 @@ class TransactionContext {
}
void setFlaggedForRollback(boolean flaggedForRollback) {
Assert.state(this.transactionStatus != null, () -> String.format(
"Failed to set rollback flag for test context %s: transaction does not exist.", this.testContext));
Assert.state(this.transactionStatus != null, () ->
"Failed to set rollback flag - transaction does not exist: " + this.testContext);
this.flaggedForRollback = flaggedForRollback;
}
@ -95,16 +97,16 @@ class TransactionContext { @@ -95,16 +97,16 @@ class TransactionContext {
*/
void startTransaction() {
Assert.state(this.transactionStatus == null,
"Cannot start a new transaction without ending the existing transaction first.");
"Cannot start a new transaction without ending the existing transaction first");
this.flaggedForRollback = this.defaultRollback;
this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition);
++this.transactionsStarted;
int transactionsStarted = this.transactionsStarted.incrementAndGet();
if (logger.isInfoEnabled()) {
logger.info(String.format(
"Began transaction (%s) for test context %s; transaction manager [%s]; rollback [%s]",
this.transactionsStarted, this.testContext, this.transactionManager, flaggedForRollback));
transactionsStarted, this.testContext, this.transactionManager, flaggedForRollback));
}
}
@ -118,8 +120,8 @@ class TransactionContext { @@ -118,8 +120,8 @@ class TransactionContext {
"Ending transaction for test context %s; transaction status [%s]; rollback [%s]",
this.testContext, this.transactionStatus, this.flaggedForRollback));
}
Assert.state(this.transactionStatus != null, () -> String.format(
"Failed to end transaction for test context %s: transaction does not exist.", this.testContext));
Assert.state(this.transactionStatus != null,
() -> "Failed to end transaction - transaction does not exist: " + this.testContext);
try {
if (this.flaggedForRollback) {
@ -134,8 +136,8 @@ class TransactionContext { @@ -134,8 +136,8 @@ class TransactionContext {
}
if (logger.isInfoEnabled()) {
logger.info(String.format("%s transaction for test context %s.",
(this.flaggedForRollback ? "Rolled back" : "Committed"), this.testContext));
logger.info((this.flaggedForRollback ? "Rolled back" : "Committed") +
" transaction for test: " + this.testContext);
}
}

6
spring-test/src/main/java/org/springframework/test/context/transaction/TransactionContextHolder.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@ -31,7 +31,7 @@ class TransactionContextHolder { @@ -31,7 +31,7 @@ class TransactionContextHolder {
new NamedInheritableThreadLocal<>("Test Transaction Context");
static void setCurrentTransactionContext(@Nullable TransactionContext transactionContext) {
static void setCurrentTransactionContext(TransactionContext transactionContext) {
currentTransactionContext.set(transactionContext);
}
@ -42,11 +42,9 @@ class TransactionContextHolder { @@ -42,11 +42,9 @@ class TransactionContextHolder {
@Nullable
static TransactionContext removeCurrentTransactionContext() {
synchronized (currentTransactionContext) {
TransactionContext transactionContext = currentTransactionContext.get();
currentTransactionContext.remove();
return transactionContext;
}
}
}

20
spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@ -136,6 +136,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis @@ -136,6 +136,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
// Do not require @Transactional test methods to be public.
protected final TransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource(false);
/**
* Returns {@code 4000}.
*/
@ -159,10 +160,10 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis @@ -159,10 +160,10 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
public void beforeTestMethod(final TestContext testContext) throws Exception {
Method testMethod = testContext.getTestMethod();
Class<?> testClass = testContext.getTestClass();
Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null");
Assert.notNull(testMethod, "Test method of supplied TestContext must not be null");
TransactionContext txContext = TransactionContextHolder.removeCurrentTransactionContext();
Assert.state(txContext == null, "Cannot start a new transaction without ending the existing transaction.");
Assert.state(txContext == null, "Cannot start new transaction without ending existing transaction");
PlatformTransactionManager tm = null;
TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod, testClass);
@ -172,8 +173,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis @@ -172,8 +173,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
transactionAttribute);
if (logger.isDebugEnabled()) {
logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context " +
testContext);
logger.debug("Explicit transaction definition [" + transactionAttribute +
"] found for test context " + testContext);
}
if (transactionAttribute.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
@ -181,9 +182,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis @@ -181,9 +182,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
}
tm = getTransactionManager(testContext, transactionAttribute.getQualifier());
Assert.state(tm != null, () -> String.format(
"Failed to retrieve PlatformTransactionManager for @Transactional test for test context %s.",
testContext));
Assert.state(tm != null,
() -> "Failed to retrieve PlatformTransactionManager for @Transactional test: " + testContext);
}
if (tm != null) {
@ -368,8 +368,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis @@ -368,8 +368,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
if (rollbackPresent) {
boolean defaultRollback = rollback.value();
if (logger.isDebugEnabled()) {
logger.debug(String.format("Retrieved default @Rollback(%s) for test class [%s].", defaultRollback,
testClass.getName()));
logger.debug(String.format("Retrieved default @Rollback(%s) for test class [%s].",
defaultRollback, testClass.getName()));
}
return defaultRollback;
}

76
spring-test/src/test/java/org/springframework/test/context/transaction/PrimaryTransactionManagerTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@ -53,38 +53,6 @@ import static org.junit.Assert.*; @@ -53,38 +53,6 @@ import static org.junit.Assert.*;
@DirtiesContext
public class PrimaryTransactionManagerTests {
@Configuration
static class Config {
@Primary
@Bean
public PlatformTransactionManager primaryTransactionManager() {
return new DataSourceTransactionManager(dataSource1());
}
@Bean
public PlatformTransactionManager additionalTransactionManager() {
return new DataSourceTransactionManager(dataSource2());
}
@Bean
public DataSource dataSource1() {
// @formatter:off
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.addScript("classpath:/org/springframework/test/context/jdbc/schema.sql")
.build();
// @formatter:on
}
@Bean
public DataSource dataSource2() {
return new EmbeddedDatabaseBuilder().generateUniqueName(true).build();
}
}
private JdbcTemplate jdbcTemplate;
@ -93,11 +61,17 @@ public class PrimaryTransactionManagerTests { @@ -93,11 +61,17 @@ public class PrimaryTransactionManagerTests {
this.jdbcTemplate = new JdbcTemplate(dataSource1);
}
@BeforeTransaction
public void beforeTransaction() {
assertNumUsers(0);
}
@AfterTransaction
public void afterTransaction() {
assertNumUsers(0);
}
@Test
@Transactional
public void transactionalTest() {
@ -109,14 +83,38 @@ public class PrimaryTransactionManagerTests { @@ -109,14 +83,38 @@ public class PrimaryTransactionManagerTests {
assertNumUsers(1);
}
@AfterTransaction
public void afterTransaction() {
assertNumUsers(0);
}
private void assertNumUsers(int expected) {
assertEquals("Number of rows in the 'user' table.", expected,
assertEquals("Number of rows in the 'user' table", expected,
JdbcTestUtils.countRowsInTable(this.jdbcTemplate, "user"));
}
@Configuration
static class Config {
@Primary
@Bean
public PlatformTransactionManager primaryTransactionManager() {
return new DataSourceTransactionManager(dataSource1());
}
@Bean
public PlatformTransactionManager additionalTransactionManager() {
return new DataSourceTransactionManager(dataSource2());
}
@Bean
public DataSource dataSource1() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.addScript("classpath:/org/springframework/test/context/jdbc/schema.sql")
.build();
}
@Bean
public DataSource dataSource2() {
return new EmbeddedDatabaseBuilder().generateUniqueName(true).build();
}
}
}

190
spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java

@ -23,8 +23,8 @@ import org.junit.After; @@ -23,8 +23,8 @@ import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.BDDMockito;
import org.springframework.beans.BeanUtils;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.annotation.Commit;
@ -51,7 +51,6 @@ public class TransactionalTestExecutionListenerTests { @@ -51,7 +51,6 @@ public class TransactionalTestExecutionListenerTests {
private final PlatformTransactionManager tm = mock(PlatformTransactionManager.class);
private final TransactionalTestExecutionListener listener = new TransactionalTestExecutionListener() {
@Override
protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) {
return tm;
@ -64,100 +63,21 @@ public class TransactionalTestExecutionListenerTests { @@ -64,100 +63,21 @@ public class TransactionalTestExecutionListenerTests {
public ExpectedException exception = ExpectedException.none();
private void assertBeforeTestMethod(Class<? extends Invocable> clazz) throws Exception {
assertBeforeTestMethodWithTransactionalTestMethod(clazz);
assertBeforeTestMethodWithNonTransactionalTestMethod(clazz);
}
private void assertBeforeTestMethodWithTransactionalTestMethod(Class<? extends Invocable> clazz) throws Exception {
assertBeforeTestMethodWithTransactionalTestMethod(clazz, true);
}
private void assertBeforeTestMethodWithTransactionalTestMethod(Class<? extends Invocable> clazz, boolean invokedInTx)
throws Exception {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
Invocable instance = BeanUtils.instantiateClass(clazz);
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("transactionalTest"));
assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
assertEquals(invokedInTx, instance.invoked());
}
private void assertBeforeTestMethodWithNonTransactionalTestMethod(Class<? extends Invocable> clazz)
throws Exception {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
Invocable instance = BeanUtils.instantiateClass(clazz);
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("nonTransactionalTest"));
assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
assertFalse("callback should not have been invoked", instance.invoked());
}
private void assertAfterTestMethod(Class<? extends Invocable> clazz) throws Exception {
assertAfterTestMethodWithTransactionalTestMethod(clazz);
assertAfterTestMethodWithNonTransactionalTestMethod(clazz);
}
private void assertAfterTestMethodWithTransactionalTestMethod(Class<? extends Invocable> clazz) throws Exception {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
Invocable instance = BeanUtils.instantiateClass(clazz);
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("transactionalTest"));
given(tm.getTransaction(BDDMockito.any(TransactionDefinition.class))).willReturn(new SimpleTransactionStatus());
assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
assertFalse("callback should not have been invoked", instance.invoked());
listener.afterTestMethod(testContext);
assertTrue("callback should have been invoked", instance.invoked());
}
private void assertAfterTestMethodWithNonTransactionalTestMethod(Class<? extends Invocable> clazz) throws Exception {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
Invocable instance = BeanUtils.instantiateClass(clazz);
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("nonTransactionalTest"));
assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
listener.afterTestMethod(testContext);
assertFalse("callback should not have been invoked", instance.invoked());
}
private void assertIsRollback(Class<?> clazz, boolean rollback) throws NoSuchMethodException, Exception {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("test"));
assertEquals(rollback, listener.isRollback(testContext));
}
@After
public void cleanUpThreadLocalStateForSubsequentTestClassesInSuite() {
TransactionContextHolder.removeCurrentTransactionContext();
}
/**
* SPR-13895
*/
@Test
@Test // SPR-13895
public void transactionalTestWithoutTransactionManager() throws Exception {
TransactionalTestExecutionListener listener = new TransactionalTestExecutionListener() {
protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) {
return null;
}
};
Class<? extends Invocable> clazz = TransactionalDeclaredOnClassLocallyTestCase.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
Invocable instance = BeanUtils.instantiateClass(clazz);
given(testContext.getTestInstance()).willReturn(instance);
@ -172,7 +92,7 @@ public class TransactionalTestExecutionListenerTests { @@ -172,7 +92,7 @@ public class TransactionalTestExecutionListenerTests {
}
catch (IllegalStateException e) {
assertTrue(e.getMessage().startsWith(
"Failed to retrieve PlatformTransactionManager for @Transactional test for test context"));
"Failed to retrieve PlatformTransactionManager for @Transactional test"));
}
}
@ -289,11 +209,84 @@ public class TransactionalTestExecutionListenerTests { @@ -289,11 +209,84 @@ public class TransactionalTestExecutionListenerTests {
}
// -------------------------------------------------------------------------
private void assertBeforeTestMethod(Class<? extends Invocable> clazz) throws Exception {
assertBeforeTestMethodWithTransactionalTestMethod(clazz);
assertBeforeTestMethodWithNonTransactionalTestMethod(clazz);
}
private void assertBeforeTestMethodWithTransactionalTestMethod(Class<? extends Invocable> clazz) throws Exception {
assertBeforeTestMethodWithTransactionalTestMethod(clazz, true);
}
private void assertBeforeTestMethodWithTransactionalTestMethod(Class<? extends Invocable> clazz, boolean invokedInTx)
throws Exception {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
Invocable instance = BeanUtils.instantiateClass(clazz);
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("transactionalTest"));
assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
assertEquals(invokedInTx, instance.invoked());
}
private void assertBeforeTestMethodWithNonTransactionalTestMethod(Class<? extends Invocable> clazz) throws Exception {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
Invocable instance = BeanUtils.instantiateClass(clazz);
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("nonTransactionalTest"));
assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
assertFalse("callback should not have been invoked", instance.invoked());
}
private void assertAfterTestMethod(Class<? extends Invocable> clazz) throws Exception {
assertAfterTestMethodWithTransactionalTestMethod(clazz);
assertAfterTestMethodWithNonTransactionalTestMethod(clazz);
}
private void assertAfterTestMethodWithTransactionalTestMethod(Class<? extends Invocable> clazz) throws Exception {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
Invocable instance = BeanUtils.instantiateClass(clazz);
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("transactionalTest"));
given(tm.getTransaction(BDDMockito.any(TransactionDefinition.class))).willReturn(new SimpleTransactionStatus());
assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
assertFalse("callback should not have been invoked", instance.invoked());
listener.afterTestMethod(testContext);
assertTrue("callback should have been invoked", instance.invoked());
}
private void assertAfterTestMethodWithNonTransactionalTestMethod(Class<? extends Invocable> clazz) throws Exception {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
Invocable instance = BeanUtils.instantiateClass(clazz);
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("nonTransactionalTest"));
assertFalse("callback should not have been invoked", instance.invoked());
TransactionContextHolder.removeCurrentTransactionContext();
listener.beforeTestMethod(testContext);
listener.afterTestMethod(testContext);
assertFalse("callback should not have been invoked", instance.invoked());
}
private void assertIsRollback(Class<?> clazz, boolean rollback) throws Exception {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("test"));
assertEquals(rollback, listener.isRollback(testContext));
}
@Transactional
@Retention(RetentionPolicy.RUNTIME)
private static @interface MetaTransactional {
private @interface MetaTransactional {
}
@Transactional
@ -308,12 +301,12 @@ public class TransactionalTestExecutionListenerTests { @@ -308,12 +301,12 @@ public class TransactionalTestExecutionListenerTests {
@BeforeTransaction
@Retention(RetentionPolicy.RUNTIME)
private static @interface MetaBeforeTransaction {
private @interface MetaBeforeTransaction {
}
@AfterTransaction
@Retention(RetentionPolicy.RUNTIME)
private static @interface MetaAfterTransaction {
private @interface MetaAfterTransaction {
}
private interface Invocable {
@ -348,7 +341,6 @@ public class TransactionalTestExecutionListenerTests { @@ -348,7 +341,6 @@ public class TransactionalTestExecutionListenerTests {
}
public void transactionalTest() {
/* no-op */
}
}
@ -361,11 +353,9 @@ public class TransactionalTestExecutionListenerTests { @@ -361,11 +353,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
@ -378,7 +368,6 @@ public class TransactionalTestExecutionListenerTests { @@ -378,7 +368,6 @@ public class TransactionalTestExecutionListenerTests {
}
public void transactionalTest() {
/* no-op */
}
}
@ -391,11 +380,9 @@ public class TransactionalTestExecutionListenerTests { @@ -391,11 +380,9 @@ public class TransactionalTestExecutionListenerTests {
@MetaTransactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
@ -408,7 +395,6 @@ public class TransactionalTestExecutionListenerTests { @@ -408,7 +395,6 @@ public class TransactionalTestExecutionListenerTests {
}
public void transactionalTest() {
/* no-op */
}
}
@ -421,11 +407,9 @@ public class TransactionalTestExecutionListenerTests { @@ -421,11 +407,9 @@ public class TransactionalTestExecutionListenerTests {
@MetaTxWithOverride(propagation = NOT_SUPPORTED)
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
@ -438,11 +422,9 @@ public class TransactionalTestExecutionListenerTests { @@ -438,11 +422,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
@ -455,11 +437,9 @@ public class TransactionalTestExecutionListenerTests { @@ -455,11 +437,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
@ -472,11 +452,9 @@ public class TransactionalTestExecutionListenerTests { @@ -472,11 +452,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
@ -489,11 +467,9 @@ public class TransactionalTestExecutionListenerTests { @@ -489,11 +467,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
@ -518,11 +494,9 @@ public class TransactionalTestExecutionListenerTests { @@ -518,11 +494,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
@ -531,11 +505,9 @@ public class TransactionalTestExecutionListenerTests { @@ -531,11 +505,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}

Loading…
Cancel
Save