Browse Source

Concurrency and exception message refinements for test transactions

(cherry picked from commit a0cc800)
pull/1723/head
Juergen Hoeller 8 years ago
parent
commit
bd6b7b08f3
  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. 64
      spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java
  4. 26
      spring-test/src/main/java/org/springframework/test/context/transaction/TestTransaction.java
  5. 31
      spring-test/src/main/java/org/springframework/test/context/transaction/TransactionContext.java
  6. 10
      spring-test/src/main/java/org/springframework/test/context/transaction/TransactionContextHolder.java
  7. 31
      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. 221
      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 @@
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -28,15 +28,15 @@ import java.lang.annotation.Target;
* configured to run within a transaction via Spring's {@code @Transactional} * configured to run within a transaction via Spring's {@code @Transactional}
* annotation. * 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 * <p>{@code @AfterTransaction} methods declared in superclasses or as interface
* default methods will be executed after those of the current test class. * 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 * <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>. * <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 * @author Sam Brannen
* @since 2.5 * @since 2.5
* @see org.springframework.transaction.annotation.Transactional * @see org.springframework.transaction.annotation.Transactional

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -28,15 +28,15 @@ import java.lang.annotation.Target;
* configured to run within a transaction via Spring's {@code @Transactional} * configured to run within a transaction via Spring's {@code @Transactional}
* annotation. * 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 * <p>{@code @BeforeTransaction} methods declared in superclasses or as interface
* default methods will be executed before those of the current test class. * 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 * <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>. * <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 * @author Sam Brannen
* @since 2.5 * @since 2.5
* @see org.springframework.transaction.annotation.Transactional * @see org.springframework.transaction.annotation.Transactional

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,6 +38,7 @@ import org.springframework.util.StringUtils;
/** /**
* Utility methods for working with transactions and data access related beans * Utility methods for working with transactions and data access related beans
* within the <em>Spring TestContext Framework</em>. * within the <em>Spring TestContext Framework</em>.
*
* <p>Mainly for internal use within the framework. * <p>Mainly for internal use within the framework.
* *
* @author Sam Brannen * @author Sam Brannen
@ -46,8 +47,6 @@ import org.springframework.util.StringUtils;
*/ */
public abstract class TestContextTransactionUtils { public abstract class TestContextTransactionUtils {
private static final Log logger = LogFactory.getLog(TestContextTransactionUtils.class);
/** /**
* Default bean name for a {@link DataSource}: {@code "dataSource"}. * Default bean name for a {@link DataSource}: {@code "dataSource"}.
*/ */
@ -60,9 +59,8 @@ public abstract class TestContextTransactionUtils {
public static final String DEFAULT_TRANSACTION_MANAGER_NAME = "transactionManager"; public static final String DEFAULT_TRANSACTION_MANAGER_NAME = "transactionManager";
private TestContextTransactionUtils() { private static final Log logger = LogFactory.getLog(TestContextTransactionUtils.class);
/* prevent instantiation */
}
/** /**
* Retrieve the {@link DataSource} to use for the supplied {@linkplain TestContext * Retrieve the {@link DataSource} to use for the supplied {@linkplain TestContext
@ -80,8 +78,8 @@ public abstract class TestContextTransactionUtils {
* {@linkplain #DEFAULT_DATA_SOURCE_NAME default data source name}. * {@linkplain #DEFAULT_DATA_SOURCE_NAME default data source name}.
* @param testContext the test context for which the {@code DataSource} * @param testContext the test context for which the {@code DataSource}
* should be retrieved; never {@code null} * should be retrieved; never {@code null}
* @param name the name of the {@code DataSource} to retrieve; may be {@code null} * @param name the name of the {@code DataSource} to retrieve
* or <em>empty</em> * (may be {@code null} or <em>empty</em>)
* @return the {@code DataSource} to use, or {@code null} if not found * @return the {@code DataSource} to use, or {@code null} if not found
* @throws BeansException if an error occurs while retrieving an explicitly * @throws BeansException if an error occurs while retrieving an explicitly
* named {@code DataSource} * named {@code DataSource}
@ -91,14 +89,14 @@ public abstract class TestContextTransactionUtils {
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory(); BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
try { try {
// look up by type and explicit name // Look up by type and explicit name
if (StringUtils.hasText(name)) { if (StringUtils.hasText(name)) {
return bf.getBean(name, DataSource.class); return bf.getBean(name, DataSource.class);
} }
} }
catch (BeansException ex) { catch (BeansException ex) {
logger.error( logger.error(String.format("Failed to retrieve DataSource named '%s' for test context %s",
String.format("Failed to retrieve DataSource named '%s' for test context %s", name, testContext), ex); name, testContext), ex);
throw ex; throw ex;
} }
@ -106,9 +104,9 @@ public abstract class TestContextTransactionUtils {
if (bf instanceof ListableBeanFactory) { if (bf instanceof ListableBeanFactory) {
ListableBeanFactory lbf = (ListableBeanFactory) bf; ListableBeanFactory lbf = (ListableBeanFactory) bf;
// look up single bean by type // Look up single bean by type
Map<String, DataSource> dataSources = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, Map<String, DataSource> dataSources =
DataSource.class); BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, DataSource.class);
if (dataSources.size() == 1) { if (dataSources.size() == 1) {
return dataSources.values().iterator().next(); return dataSources.values().iterator().next();
} }
@ -150,8 +148,8 @@ public abstract class TestContextTransactionUtils {
* name}. * name}.
* @param testContext the test context for which the transaction manager * @param testContext the test context for which the transaction manager
* should be retrieved; never {@code null} * should be retrieved; never {@code null}
* @param name the name of the transaction manager to retrieve; may be * @param name the name of the transaction manager to retrieve
* {@code null} or <em>empty</em> * (may be {@code null} or <em>empty</em>)
* @return the transaction manager to use, or {@code null} if not found * @return the transaction manager to use, or {@code null} if not found
* @throws BeansException if an error occurs while retrieving an explicitly * @throws BeansException if an error occurs while retrieving an explicitly
* named transaction manager * named transaction manager
@ -163,14 +161,14 @@ public abstract class TestContextTransactionUtils {
BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory(); BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
try { try {
// look up by type and explicit name // Look up by type and explicit name
if (StringUtils.hasText(name)) { if (StringUtils.hasText(name)) {
return bf.getBean(name, PlatformTransactionManager.class); return bf.getBean(name, PlatformTransactionManager.class);
} }
} }
catch (BeansException ex) { catch (BeansException ex) {
logger.error(String.format("Failed to retrieve transaction manager named '%s' for test context %s", name, logger.error(String.format("Failed to retrieve transaction manager named '%s' for test context %s",
testContext), ex); name, testContext), ex);
throw ex; throw ex;
} }
@ -178,28 +176,26 @@ public abstract class TestContextTransactionUtils {
if (bf instanceof ListableBeanFactory) { if (bf instanceof ListableBeanFactory) {
ListableBeanFactory lbf = (ListableBeanFactory) bf; ListableBeanFactory lbf = (ListableBeanFactory) bf;
// look up single bean by type // Look up single bean by type
Map<String, PlatformTransactionManager> txMgrs = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, Map<String, PlatformTransactionManager> txMgrs =
PlatformTransactionManager.class); BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, PlatformTransactionManager.class);
if (txMgrs.size() == 1) { if (txMgrs.size() == 1) {
return txMgrs.values().iterator().next(); return txMgrs.values().iterator().next();
} }
try { 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); return bf.getBean(PlatformTransactionManager.class);
} }
catch (BeansException ex) { catch (BeansException ex) {
logBeansException(testContext, ex, PlatformTransactionManager.class); logBeansException(testContext, ex, PlatformTransactionManager.class);
} }
// look up single TransactionManagementConfigurer // Look up single TransactionManagementConfigurer
Map<String, TransactionManagementConfigurer> configurers = BeanFactoryUtils.beansOfTypeIncludingAncestors( Map<String, TransactionManagementConfigurer> configurers =
lbf, TransactionManagementConfigurer.class); BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, TransactionManagementConfigurer.class);
if (configurers.size() > 1) { Assert.state(configurers.size() <= 1,
throw new IllegalStateException(
"Only one TransactionManagementConfigurer may exist in the ApplicationContext"); "Only one TransactionManagementConfigurer may exist in the ApplicationContext");
}
if (configurers.size() == 1) { if (configurers.size() == 1) {
return configurers.values().iterator().next().annotationDrivenTransactionManager(); return configurers.values().iterator().next().annotationDrivenTransactionManager();
} }
@ -225,13 +221,13 @@ public abstract class TestContextTransactionUtils {
* Create a delegating {@link TransactionAttribute} for the supplied target * Create a delegating {@link TransactionAttribute} for the supplied target
* {@link TransactionAttribute} and {@link TestContext}, using the names of * {@link TransactionAttribute} and {@link TestContext}, using the names of
* the test class and test method to build the name of the transaction. * the test class and test method to build the name of the transaction.
* * @param testContext the {@code TestContext} upon which to base the name
* @param testContext the {@code TestContext} upon which to base the name; never {@code null} * @param targetAttribute the {@code TransactionAttribute} to delegate to
* @param targetAttribute the {@code TransactionAttribute} to delegate to; never {@code null}
* @return the delegating {@code TransactionAttribute} * @return the delegating {@code TransactionAttribute}
*/ */
public static TransactionAttribute createDelegatingTransactionAttribute(TestContext testContext, public static TransactionAttribute createDelegatingTransactionAttribute(
TransactionAttribute targetAttribute) { TestContext testContext, TransactionAttribute targetAttribute) {
Assert.notNull(testContext, "TestContext must not be null"); Assert.notNull(testContext, "TestContext must not be null");
Assert.notNull(targetAttribute, "Target TransactionAttribute must not be null"); Assert.notNull(targetAttribute, "Target TransactionAttribute must not be null");
return new TestContextTransactionAttribute(targetAttribute, testContext); return new TestContextTransactionAttribute(targetAttribute, testContext);

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.test.context.transaction;
import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.TestExecutionListeners;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.util.Assert;
/** /**
* {@code TestTransaction} provides a collection of static utility methods for * {@code TestTransaction} provides a collection of static utility methods for
@ -49,10 +50,8 @@ public class TestTransaction {
TransactionContext transactionContext = TransactionContextHolder.getCurrentTransactionContext(); TransactionContext transactionContext = TransactionContextHolder.getCurrentTransactionContext();
if (transactionContext != null) { if (transactionContext != null) {
TransactionStatus transactionStatus = transactionContext.getTransactionStatus(); TransactionStatus transactionStatus = transactionContext.getTransactionStatus();
return (transactionStatus != null) && (!transactionStatus.isCompleted()); return (transactionStatus != null && !transactionStatus.isCompleted());
} }
// else
return false; return false;
} }
@ -79,8 +78,7 @@ public class TestTransaction {
* Rather, the value of this flag will be used to determine whether or not * 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 * the current test-managed transaction should be rolled back or committed
* once it is {@linkplain #end ended}. * once it is {@linkplain #end ended}.
* @throws IllegalStateException if a transaction is not active for the * @throws IllegalStateException if no transaction is active for the current test
* current test
* @see #isActive() * @see #isActive()
* @see #isFlaggedForRollback() * @see #isFlaggedForRollback()
* @see #start() * @see #start()
@ -96,8 +94,7 @@ public class TestTransaction {
* Rather, the value of this flag will be used to determine whether or not * 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 * the current test-managed transaction should be rolled back or committed
* once it is {@linkplain #end ended}. * once it is {@linkplain #end ended}.
* @throws IllegalStateException if a transaction is not active for the * @throws IllegalStateException if no transaction is active for the current test
* current test
* @see #isActive() * @see #isActive()
* @see #isFlaggedForRollback() * @see #isFlaggedForRollback()
* @see #start() * @see #start()
@ -121,9 +118,9 @@ public class TestTransaction {
} }
/** /**
* Immediately force a <em>commit</em> or <em>rollback</em> of the current * Immediately force a <em>commit</em> or <em>rollback</em> of the
* test-managed transaction, according to the {@linkplain #isFlaggedForRollback * current test-managed transaction, according to the
* rollback flag}. * {@linkplain #isFlaggedForRollback rollback flag}.
* @throws IllegalStateException if the transaction context could not be * @throws IllegalStateException if the transaction context could not be
* retrieved or if a transaction is not active for the current test * retrieved or if a transaction is not active for the current test
* @see #isActive() * @see #isActive()
@ -133,11 +130,10 @@ public class TestTransaction {
requireCurrentTransactionContext().endTransaction(); requireCurrentTransactionContext().endTransaction();
} }
private static TransactionContext requireCurrentTransactionContext() { private static TransactionContext requireCurrentTransactionContext() {
TransactionContext txContext = TransactionContextHolder.getCurrentTransactionContext(); TransactionContext txContext = TransactionContextHolder.getCurrentTransactionContext();
if (txContext == null) { Assert.state(txContext != null, "TransactionContext is not active");
throw new IllegalStateException("TransactionContext is not active");
}
return txContext; return txContext;
} }
@ -145,4 +141,4 @@ public class TestTransaction {
requireCurrentTransactionContext().setFlaggedForRollback(flag); requireCurrentTransactionContext().setFlaggedForRollback(flag);
} }
} }

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,8 @@
package org.springframework.test.context.transaction; package org.springframework.test.context.transaction;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -24,6 +26,7 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.util.Assert;
/** /**
* Transaction context for a specific {@link TestContext}. * Transaction context for a specific {@link TestContext}.
@ -50,7 +53,7 @@ class TransactionContext {
private TransactionStatus transactionStatus; private TransactionStatus transactionStatus;
private volatile int transactionsStarted = 0; private final AtomicInteger transactionsStarted = new AtomicInteger(0);
TransactionContext(TestContext testContext, PlatformTransactionManager transactionManager, TransactionContext(TestContext testContext, PlatformTransactionManager transactionManager,
@ -79,32 +82,30 @@ class TransactionContext {
void setFlaggedForRollback(boolean flaggedForRollback) { void setFlaggedForRollback(boolean flaggedForRollback) {
if (this.transactionStatus == null) { if (this.transactionStatus == null) {
throw new IllegalStateException(String.format( throw new IllegalStateException(
"Failed to set rollback flag for test context %s: transaction does not exist.", this.testContext)); "Failed to set rollback flag - transaction does not exist: " + this.testContext);
} }
this.flaggedForRollback = flaggedForRollback; this.flaggedForRollback = flaggedForRollback;
} }
/** /**
* Start a new transaction for the configured {@linkplain #getTestContext test context}. * Start a new transaction for the configured test context.
* <p>Only call this method if {@link #endTransaction} has been called or if no * <p>Only call this method if {@link #endTransaction} has been called or if no
* transaction has been previously started. * transaction has been previously started.
* @throws TransactionException if starting the transaction fails * @throws TransactionException if starting the transaction fails
*/ */
void startTransaction() { void startTransaction() {
if (this.transactionStatus != null) { Assert.state(this.transactionStatus == null,
throw new IllegalStateException( "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.flaggedForRollback = this.defaultRollback;
this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition); this.transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition);
++this.transactionsStarted; int transactionsStarted = this.transactionsStarted.incrementAndGet();
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info(String.format( logger.info(String.format(
"Began transaction (%s) for test context %s; transaction manager [%s]; rollback [%s]", "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));
} }
} }
@ -119,8 +120,8 @@ class TransactionContext {
this.testContext, this.transactionStatus, this.flaggedForRollback)); this.testContext, this.transactionStatus, this.flaggedForRollback));
} }
if (this.transactionStatus == null) { if (this.transactionStatus == null) {
throw new IllegalStateException(String.format( throw new IllegalStateException(
"Failed to end transaction for test context %s: transaction does not exist.", this.testContext)); "Failed to end transaction - transaction does not exist: " + this.testContext);
} }
try { try {
@ -136,8 +137,8 @@ class TransactionContext {
} }
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
logger.info(String.format("%s transaction for test context %s.", logger.info((this.flaggedForRollback ? "Rolled back" : "Committed") +
(this.flaggedForRollback ? "Rolled back" : "Committed"), this.testContext)); " transaction for test: " + this.testContext);
} }
} }

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,11 +39,9 @@ class TransactionContextHolder {
} }
static TransactionContext removeCurrentTransactionContext() { static TransactionContext removeCurrentTransactionContext() {
synchronized (currentTransactionContext) { TransactionContext transactionContext = currentTransactionContext.get();
TransactionContext transactionContext = currentTransactionContext.get(); currentTransactionContext.remove();
currentTransactionContext.remove(); return transactionContext;
return transactionContext;
}
} }
} }

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -163,14 +163,12 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
*/ */
@Override @Override
public void beforeTestMethod(final TestContext testContext) throws Exception { public void beforeTestMethod(final TestContext testContext) throws Exception {
final Method testMethod = testContext.getTestMethod(); Method testMethod = testContext.getTestMethod();
final Class<?> testClass = testContext.getTestClass(); 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(); TransactionContext txContext = TransactionContextHolder.removeCurrentTransactionContext();
if (txContext != null) { Assert.state(txContext == null, "Cannot start new transaction without ending existing transaction");
throw new IllegalStateException("Cannot start a new transaction without ending the existing transaction.");
}
PlatformTransactionManager tm = null; PlatformTransactionManager tm = null;
TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod, testClass); TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod, testClass);
@ -180,8 +178,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
transactionAttribute); transactionAttribute);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context " + logger.debug("Explicit transaction definition [" + transactionAttribute +
testContext); "] found for test context " + testContext);
} }
if (transactionAttribute.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { if (transactionAttribute.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
@ -191,9 +189,8 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
tm = getTransactionManager(testContext, transactionAttribute.getQualifier()); tm = getTransactionManager(testContext, transactionAttribute.getQualifier());
if (tm == null) { if (tm == null) {
throw new IllegalStateException(String.format( throw new IllegalStateException(
"Failed to retrieve PlatformTransactionManager for @Transactional test for test context %s.", "Failed to retrieve PlatformTransactionManager for @Transactional test: " + testContext);
testContext));
} }
} }
@ -223,7 +220,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
TransactionStatus transactionStatus = txContext.getTransactionStatus(); TransactionStatus transactionStatus = txContext.getTransactionStatus();
try { try {
// If the transaction is still active... // If the transaction is still active...
if ((transactionStatus != null) && !transactionStatus.isCompleted()) { if (transactionStatus != null && !transactionStatus.isCompleted()) {
txContext.endTransaction(); txContext.endTransaction();
} }
} }
@ -380,15 +377,15 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
TransactionConfigurationAttributes txConfigAttributes = retrieveConfigurationAttributes(testContext); TransactionConfigurationAttributes txConfigAttributes = retrieveConfigurationAttributes(testContext);
if (rollbackPresent && txConfigAttributes != defaultTxConfigAttributes) { if (rollbackPresent && txConfigAttributes != defaultTxConfigAttributes) {
throw new IllegalStateException(String.format("Test class [%s] is annotated with both @Rollback " throw new IllegalStateException(String.format("Test class [%s] is annotated with both @Rollback " +
+ "and @TransactionConfiguration, but only one is permitted.", testClass.getName())); "and @TransactionConfiguration, but only one is permitted.", testClass.getName()));
} }
if (rollbackPresent) { if (rollbackPresent) {
boolean defaultRollback = rollback.value(); boolean defaultRollback = rollback.value();
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug(String.format("Retrieved default @Rollback(%s) for test class [%s].", defaultRollback, logger.debug(String.format("Retrieved default @Rollback(%s) for test class [%s].",
testClass.getName())); defaultRollback, testClass.getName()));
} }
return defaultRollback; return defaultRollback;
} }

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -53,38 +53,6 @@ import static org.junit.Assert.*;
@DirtiesContext @DirtiesContext
public class PrimaryTransactionManagerTests { 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; private JdbcTemplate jdbcTemplate;
@ -93,11 +61,17 @@ public class PrimaryTransactionManagerTests {
this.jdbcTemplate = new JdbcTemplate(dataSource1); this.jdbcTemplate = new JdbcTemplate(dataSource1);
} }
@BeforeTransaction @BeforeTransaction
public void beforeTransaction() { public void beforeTransaction() {
assertNumUsers(0); assertNumUsers(0);
} }
@AfterTransaction
public void afterTransaction() {
assertNumUsers(0);
}
@Test @Test
@Transactional @Transactional
public void transactionalTest() { public void transactionalTest() {
@ -109,14 +83,38 @@ public class PrimaryTransactionManagerTests {
assertNumUsers(1); assertNumUsers(1);
} }
@AfterTransaction private void assertNumUsers(int expected) {
public void afterTransaction() { assertEquals("Number of rows in the 'user' table", expected,
assertNumUsers(0); JdbcTestUtils.countRowsInTable(this.jdbcTemplate, "user"));
} }
private void assertNumUsers(int expected) {
assertEquals("Number of rows in the 'user' table.", expected, @Configuration
JdbcTestUtils.countRowsInTable(this.jdbcTemplate, "user")); 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();
}
} }
} }

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

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,7 +23,6 @@ import org.junit.After;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.mockito.BDDMockito; import org.mockito.BDDMockito;
import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.AliasFor;
@ -53,7 +52,6 @@ public class TransactionalTestExecutionListenerTests {
private final PlatformTransactionManager tm = mock(PlatformTransactionManager.class); private final PlatformTransactionManager tm = mock(PlatformTransactionManager.class);
private final TransactionalTestExecutionListener listener = new TransactionalTestExecutionListener() { private final TransactionalTestExecutionListener listener = new TransactionalTestExecutionListener() {
@Override @Override
protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) { protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) {
return tm; return tm;
@ -66,110 +64,21 @@ public class TransactionalTestExecutionListenerTests {
public ExpectedException exception = ExpectedException.none(); 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 = clazz.newInstance();
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 = clazz.newInstance();
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 = clazz.newInstance();
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 = clazz.newInstance();
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 assertTransactionConfigurationAttributes(Class<?> clazz, String transactionManagerName,
boolean defaultRollback) {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
TransactionConfigurationAttributes attributes = listener.retrieveConfigurationAttributes(testContext);
assertNotNull(attributes);
assertEquals(transactionManagerName, attributes.getTransactionManagerName());
assertEquals(defaultRollback, attributes.isDefaultRollback());
}
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 @After
public void cleanUpThreadLocalStateForSubsequentTestClassesInSuite() { public void cleanUpThreadLocalStateForSubsequentTestClassesInSuite() {
TransactionContextHolder.removeCurrentTransactionContext(); TransactionContextHolder.removeCurrentTransactionContext();
} }
/**
* SPR-13895 @Test // SPR-13895
*/
@Test
public void transactionalTestWithoutTransactionManager() throws Exception { public void transactionalTestWithoutTransactionManager() throws Exception {
TransactionalTestExecutionListener listener = new TransactionalTestExecutionListener() { TransactionalTestExecutionListener listener = new TransactionalTestExecutionListener() {
protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) { protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) {
return null; return null;
} }
}; };
Class<? extends Invocable> clazz = TransactionalDeclaredOnClassLocallyTestCase.class; Class<? extends Invocable> clazz = TransactionalDeclaredOnClassLocallyTestCase.class;
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz); BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
Invocable instance = clazz.newInstance(); Invocable instance = clazz.newInstance();
given(testContext.getTestInstance()).willReturn(instance); given(testContext.getTestInstance()).willReturn(instance);
@ -184,7 +93,7 @@ public class TransactionalTestExecutionListenerTests {
} }
catch (IllegalStateException e) { catch (IllegalStateException e) {
assertTrue(e.getMessage().startsWith( assertTrue(e.getMessage().startsWith(
"Failed to retrieve PlatformTransactionManager for @Transactional test for test context")); "Failed to retrieve PlatformTransactionManager for @Transactional test"));
} }
} }
@ -203,7 +112,7 @@ public class TransactionalTestExecutionListenerTests {
// Note: not actually invoked within a transaction since the test class is // Note: not actually invoked within a transaction since the test class is
// annotated with @MetaTxWithOverride(propagation = NOT_SUPPORTED) // annotated with @MetaTxWithOverride(propagation = NOT_SUPPORTED)
assertBeforeTestMethodWithTransactionalTestMethod( assertBeforeTestMethodWithTransactionalTestMethod(
TransactionalDeclaredOnClassViaMetaAnnotationWithOverrideTestCase.class, false); TransactionalDeclaredOnClassViaMetaAnnotationWithOverrideTestCase.class, false);
} }
@Test @Test
@ -211,7 +120,7 @@ public class TransactionalTestExecutionListenerTests {
// Note: not actually invoked within a transaction since the method is // Note: not actually invoked within a transaction since the method is
// annotated with @MetaTxWithOverride(propagation = NOT_SUPPORTED) // annotated with @MetaTxWithOverride(propagation = NOT_SUPPORTED)
assertBeforeTestMethodWithTransactionalTestMethod( assertBeforeTestMethodWithTransactionalTestMethod(
TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase.class, false); TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase.class, false);
assertBeforeTestMethodWithNonTransactionalTestMethod(TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase.class); assertBeforeTestMethodWithNonTransactionalTestMethod(TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase.class);
} }
@ -315,8 +224,7 @@ public class TransactionalTestExecutionListenerTests {
// @TransactionConfiguration. So we actually expect "" as the qualifier here, // @TransactionConfiguration. So we actually expect "" as the qualifier here,
// relying on beforeTestMethod() to properly obtain the actual qualifier via the // relying on beforeTestMethod() to properly obtain the actual qualifier via the
// TransactionAttribute. // TransactionAttribute.
assertTransactionConfigurationAttributes(TransactionalViaMetaAnnotationWithExplicitQualifierTestCase.class, "", assertTransactionConfigurationAttributes(TransactionalViaMetaAnnotationWithExplicitQualifierTestCase.class, "", true);
true);
} }
@Test @Test
@ -375,11 +283,95 @@ 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 = clazz.newInstance();
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 = clazz.newInstance();
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 = clazz.newInstance();
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 = clazz.newInstance();
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 assertTransactionConfigurationAttributes(
Class<?> clazz, String transactionManagerName, boolean defaultRollback) {
BDDMockito.<Class<?>> given(testContext.getTestClass()).willReturn(clazz);
TransactionConfigurationAttributes attributes = listener.retrieveConfigurationAttributes(testContext);
assertNotNull(attributes);
assertEquals(transactionManagerName, attributes.getTransactionManagerName());
assertEquals(defaultRollback, attributes.isDefaultRollback());
}
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));
}
@Transactional @Transactional
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
private static @interface MetaTransactional { private @interface MetaTransactional {
} }
@Transactional @Transactional
@ -394,17 +386,17 @@ public class TransactionalTestExecutionListenerTests {
@BeforeTransaction @BeforeTransaction
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
private static @interface MetaBeforeTransaction { private @interface MetaBeforeTransaction {
} }
@AfterTransaction @AfterTransaction
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
private static @interface MetaAfterTransaction { private @interface MetaAfterTransaction {
} }
@TransactionConfiguration @TransactionConfiguration
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
private static @interface MetaTxConfig { private @interface MetaTxConfig {
String transactionManager() default "metaTxMgr"; String transactionManager() default "metaTxMgr";
} }
@ -441,7 +433,6 @@ public class TransactionalTestExecutionListenerTests {
} }
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
} }
@ -454,11 +445,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional @Transactional
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
public void nonTransactionalTest() { public void nonTransactionalTest() {
/* no-op */
} }
} }
@ -471,7 +460,6 @@ public class TransactionalTestExecutionListenerTests {
} }
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
} }
@ -484,11 +472,9 @@ public class TransactionalTestExecutionListenerTests {
@MetaTransactional @MetaTransactional
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
public void nonTransactionalTest() { public void nonTransactionalTest() {
/* no-op */
} }
} }
@ -501,7 +487,6 @@ public class TransactionalTestExecutionListenerTests {
} }
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
} }
@ -514,11 +499,9 @@ public class TransactionalTestExecutionListenerTests {
@MetaTxWithOverride(propagation = NOT_SUPPORTED) @MetaTxWithOverride(propagation = NOT_SUPPORTED)
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
public void nonTransactionalTest() { public void nonTransactionalTest() {
/* no-op */
} }
} }
@ -531,11 +514,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional @Transactional
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
public void nonTransactionalTest() { public void nonTransactionalTest() {
/* no-op */
} }
} }
@ -548,11 +529,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional @Transactional
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
public void nonTransactionalTest() { public void nonTransactionalTest() {
/* no-op */
} }
} }
@ -565,11 +544,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional @Transactional
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
public void nonTransactionalTest() { public void nonTransactionalTest() {
/* no-op */
} }
} }
@ -582,11 +559,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional @Transactional
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
public void nonTransactionalTest() { public void nonTransactionalTest() {
/* no-op */
} }
} }
@ -611,11 +586,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional @Transactional
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
public void nonTransactionalTest() { public void nonTransactionalTest() {
/* no-op */
} }
} }
@ -624,11 +597,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional @Transactional
public void transactionalTest() { public void transactionalTest() {
/* no-op */
} }
public void nonTransactionalTest() { public void nonTransactionalTest() {
/* no-op */
} }
} }

Loading…
Cancel
Save