Concurrency and exception message refinements for test transactions

This commit is contained in:
Juergen Hoeller
2018-03-05 13:00:35 +01:00
parent ff818d56a4
commit a0cc80063d
9 changed files with 194 additions and 230 deletions

View File

@@ -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,6 +53,42 @@ import static org.junit.Assert.*;
@DirtiesContext
public class PrimaryTransactionManagerTests {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource1) {
this.jdbcTemplate = new JdbcTemplate(dataSource1);
}
@BeforeTransaction
public void beforeTransaction() {
assertNumUsers(0);
}
@AfterTransaction
public void afterTransaction() {
assertNumUsers(0);
}
@Test
@Transactional
public void transactionalTest() {
TransactionTestUtils.assertInTransaction(true);
ClassPathResource resource = new ClassPathResource("/org/springframework/test/context/jdbc/data.sql");
new ResourceDatabasePopulator(resource).execute(jdbcTemplate.getDataSource());
assertNumUsers(1);
}
private void assertNumUsers(int expected) {
assertEquals("Number of rows in the 'user' table", expected,
JdbcTestUtils.countRowsInTable(this.jdbcTemplate, "user"));
}
@Configuration
static class Config {
@@ -69,54 +105,16 @@ public class PrimaryTransactionManagerTests {
@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;
@Autowired
public void setDataSource(DataSource dataSource1) {
this.jdbcTemplate = new JdbcTemplate(dataSource1);
}
@BeforeTransaction
public void beforeTransaction() {
assertNumUsers(0);
}
@Test
@Transactional
public void transactionalTest() {
TransactionTestUtils.assertInTransaction(true);
ClassPathResource resource = new ClassPathResource("/org/springframework/test/context/jdbc/data.sql");
new ResourceDatabasePopulator(resource).execute(jdbcTemplate.getDataSource());
assertNumUsers(1);
}
@AfterTransaction
public void afterTransaction() {
assertNumUsers(0);
}
private void assertNumUsers(int expected) {
assertEquals("Number of rows in the 'user' table.", expected,
JdbcTestUtils.countRowsInTable(this.jdbcTemplate, "user"));
}
}

View File

@@ -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 {
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 {
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 {
}
catch (IllegalStateException e) {
assertTrue(e.getMessage().startsWith(
"Failed to retrieve PlatformTransactionManager for @Transactional test for test context"));
"Failed to retrieve PlatformTransactionManager for @Transactional test"));
}
}
@@ -191,7 +111,7 @@ public class TransactionalTestExecutionListenerTests {
// Note: not actually invoked within a transaction since the test class is
// annotated with @MetaTxWithOverride(propagation = NOT_SUPPORTED)
assertBeforeTestMethodWithTransactionalTestMethod(
TransactionalDeclaredOnClassViaMetaAnnotationWithOverrideTestCase.class, false);
TransactionalDeclaredOnClassViaMetaAnnotationWithOverrideTestCase.class, false);
}
@Test
@@ -199,7 +119,7 @@ public class TransactionalTestExecutionListenerTests {
// Note: not actually invoked within a transaction since the method is
// annotated with @MetaTxWithOverride(propagation = NOT_SUPPORTED)
assertBeforeTestMethodWithTransactionalTestMethod(
TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase.class, false);
TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase.class, false);
assertBeforeTestMethodWithNonTransactionalTestMethod(TransactionalDeclaredOnMethodViaMetaAnnotationWithOverrideTestCase.class);
}
@@ -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 {
@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 {
}
public void transactionalTest() {
/* no-op */
}
}
@@ -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 {
}
public void transactionalTest() {
/* no-op */
}
}
@@ -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 {
}
public void transactionalTest() {
/* no-op */
}
}
@@ -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 {
@Transactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
@@ -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 {
@Transactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
@@ -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 {
@Transactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}
@@ -531,11 +505,9 @@ public class TransactionalTestExecutionListenerTests {
@Transactional
public void transactionalTest() {
/* no-op */
}
public void nonTransactionalTest() {
/* no-op */
}
}