diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/AbstractTransactionalAnnotatedConfigClassTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/AbstractTransactionalAnnotatedConfigClassTests.java new file mode 100644 index 000000000000..0c6580ee574f --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/AbstractTransactionalAnnotatedConfigClassTests.java @@ -0,0 +1,167 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.junit4.spr9051; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.springframework.test.transaction.TransactionTestUtils.assertInTransaction; +import static org.springframework.test.transaction.TransactionTestUtils.inTransaction; + +import javax.sql.DataSource; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.Employee; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.transaction.AfterTransaction; +import org.springframework.test.context.transaction.BeforeTransaction; +import org.springframework.transaction.annotation.Transactional; + +/** + * This set of tests investigates the claims made in + * SPR-9051 + * with regard to transactional tests. + * + * @author Sam Brannen + * @since 3.2 + * @see org.springframework.test.context.testng.AnnotationConfigTransactionalTestNGSpringContextTests + */ +@RunWith(SpringJUnit4ClassRunner.class) +public abstract class AbstractTransactionalAnnotatedConfigClassTests { + + protected static final String JANE = "jane"; + protected static final String SUE = "sue"; + protected static final String YODA = "yoda"; + + protected static final int NUM_TESTS = 2; + protected static final int NUM_TX_TESTS = 1; + + private static int numSetUpCalls = 0; + private static int numSetUpCallsInTransaction = 0; + private static int numTearDownCalls = 0; + private static int numTearDownCallsInTransaction = 0; + + protected DataSource dataSourceFromTxManager; + protected DataSource dataSourceViaInjection; + + protected JdbcTemplate jdbcTemplate; + + @Autowired + private Employee employee; + + + @Autowired + public void setTransactionManager(DataSourceTransactionManager transactionManager) { + this.dataSourceFromTxManager = transactionManager.getDataSource(); + } + + @Autowired + public void setDataSource(DataSource dataSource) { + this.dataSourceViaInjection = dataSource; + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + protected int countRowsInTable(String tableName) { + return jdbcTemplate.queryForInt("SELECT COUNT(0) FROM " + tableName); + } + + protected int createPerson(String name) { + return jdbcTemplate.update("INSERT INTO person VALUES(?)", name); + } + + protected int deletePerson(String name) { + return jdbcTemplate.update("DELETE FROM person WHERE name=?", name); + } + + protected void assertNumRowsInPersonTable(int expectedNumRows, String testState) { + assertEquals("the number of rows in the person table (" + testState + ").", expectedNumRows, + countRowsInTable("person")); + } + + protected void assertAddPerson(final String name) { + assertEquals("Adding '" + name + "'", 1, createPerson(name)); + } + + @BeforeClass + public static void beforeClass() { + numSetUpCalls = 0; + numSetUpCallsInTransaction = 0; + numTearDownCalls = 0; + numTearDownCallsInTransaction = 0; + } + + @AfterClass + public static void afterClass() { + assertEquals("number of calls to setUp().", NUM_TESTS, numSetUpCalls); + assertEquals("number of calls to setUp() within a transaction.", NUM_TX_TESTS, numSetUpCallsInTransaction); + assertEquals("number of calls to tearDown().", NUM_TESTS, numTearDownCalls); + assertEquals("number of calls to tearDown() within a transaction.", NUM_TX_TESTS, numTearDownCallsInTransaction); + } + + @Test + public void autowiringFromConfigClass() { + assertNotNull("The employee should have been autowired.", employee); + assertEquals("John Smith", employee.getName()); + } + + @BeforeTransaction + public void beforeTransaction() { + assertNumRowsInPersonTable(0, "before a transactional test method"); + assertAddPerson(YODA); + } + + @Before + public void setUp() throws Exception { + numSetUpCalls++; + if (inTransaction()) { + numSetUpCallsInTransaction++; + } + assertNumRowsInPersonTable((inTransaction() ? 1 : 0), "before a test method"); + } + + @Test + @Transactional + public void modifyTestDataWithinTransaction() { + assertInTransaction(true); + assertAddPerson(JANE); + assertAddPerson(SUE); + assertNumRowsInPersonTable(3, "in modifyTestDataWithinTransaction()"); + } + + @After + public void tearDown() throws Exception { + numTearDownCalls++; + if (inTransaction()) { + numTearDownCallsInTransaction++; + } + assertNumRowsInPersonTable((inTransaction() ? 3 : 0), "after a test method"); + } + + @AfterTransaction + public void afterTransaction() { + assertEquals("Deleting yoda", 1, deletePerson(YODA)); + assertNumRowsInPersonTable(0, "after a transactional test method"); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/TransactionalAnnotatedConfigClassWithAtConfigurationTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/TransactionalAnnotatedConfigClassWithAtConfigurationTests.java new file mode 100644 index 000000000000..262c6f7a99d4 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/TransactionalAnnotatedConfigClassWithAtConfigurationTests.java @@ -0,0 +1,83 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.junit4.spr9051; + +import static org.junit.Assert.assertSame; + +import javax.sql.DataSource; + +import org.junit.Before; +import org.springframework.beans.Employee; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * Concrete implementation of {@link AbstractTransactionalAnnotatedConfigClassTests} + * that uses a true {@link Configuration @Configuration class}. + * + * @author Sam Brannen + * @since 3.2 + * @see TransactionalAnnotatedConfigClassesWithoutAtConfigurationTests + */ +@ContextConfiguration +public class TransactionalAnnotatedConfigClassWithAtConfigurationTests extends + AbstractTransactionalAnnotatedConfigClassTests { + + /** + * This is intentionally annotated with {@code @Configuration}. + * + *
Consequently, this class contains standard singleton bean methods + * instead of annotated factory bean methods. + */ + @Configuration + static class Config { + + @Bean + public Employee employee() { + Employee employee = new Employee(); + employee.setName("John Smith"); + employee.setAge(42); + employee.setCompany("Acme Widgets, Inc."); + return employee; + } + + @Bean + public PlatformTransactionManager transactionManager() { + return new DataSourceTransactionManager(dataSource()); + } + + @Bean + public DataSource dataSource() { + return new EmbeddedDatabaseBuilder()// + .addScript("classpath:/org/springframework/test/context/junit4/spr9051/schema.sql")// + .build(); + } + + } + + + @Before + public void compareDataSources() throws Exception { + // NOTE: the two DataSource instances are the same! + assertSame(dataSourceFromTxManager, dataSourceViaInjection); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/TransactionalAnnotatedConfigClassesWithoutAtConfigurationTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/TransactionalAnnotatedConfigClassesWithoutAtConfigurationTests.java new file mode 100644 index 000000000000..76c50d39b6e5 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/TransactionalAnnotatedConfigClassesWithoutAtConfigurationTests.java @@ -0,0 +1,136 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.junit4.spr9051; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; + +import javax.sql.DataSource; + +import org.junit.Before; +import org.junit.runner.RunWith; +import org.springframework.beans.Employee; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.transaction.AfterTransaction; +import org.springframework.test.context.transaction.TransactionalTestExecutionListener; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * Concrete implementation of {@link AbstractTransactionalAnnotatedConfigClassTests} + * that does not use a true {@link Configuration @Configuration class} but + * rather a lite mode configuration class (see the Javadoc for {@link Bean @Bean} + * for details). + * + * @author Sam Brannen + * @since 3.2 + * @see Bean + * @see TransactionalAnnotatedConfigClassWithAtConfigurationTests + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TransactionalAnnotatedConfigClassesWithoutAtConfigurationTests.AnnotatedFactoryBeans.class) +public class TransactionalAnnotatedConfigClassesWithoutAtConfigurationTests extends + AbstractTransactionalAnnotatedConfigClassTests { + + /** + * This is intentionally not annotated with {@code @Configuration}. + * + *
Consequently, this class contains annotated factory bean methods + * instead of standard singleton bean methods. + */ + // @Configuration + static class AnnotatedFactoryBeans { + + @Bean + public Employee employee() { + Employee employee = new Employee(); + employee.setName("John Smith"); + employee.setAge(42); + employee.setCompany("Acme Widgets, Inc."); + return employee; + } + + @Bean + public PlatformTransactionManager transactionManager() { + return new DataSourceTransactionManager(dataSource()); + } + + /** + * Since this method does not reside in a true {@code @Configuration class}, + * it acts as a factory method instead of a singleton bean. The result is + * that this method will be called at least twice: + * + *
See in-line comments for details. + * + * @see AbstractTransactionalAnnotatedConfigClassTests#afterTransaction() + * @see AbstractTransactionalAnnotatedConfigClassTests#modifyTestDataWithinTransaction() + */ + @AfterTransaction + @Override + public void afterTransaction() { + assertEquals("Deleting yoda", 1, deletePerson(YODA)); + + // NOTE: We would actually expect that there are now ZERO entries in the + // person table, since the transaction is rolled back by the framework; + // however, since our JdbcTemplate and the transaction manager used by + // the Spring TestContext Framework use two different DataSource + // instances, our insert statements were executed in transactions that + // are not controlled by the test framework. Consequently, there was no + // rollback for the two insert statements in + // modifyTestDataWithinTransaction(). + // + assertNumRowsInPersonTable(2, "after a transactional test method"); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/schema.sql b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/schema.sql new file mode 100644 index 000000000000..81d7e08db921 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/spr9051/schema.sql @@ -0,0 +1,6 @@ +DROP TABLE person IF EXISTS; + +CREATE TABLE person ( + name VARCHAR(20) NOT NULL, + PRIMARY KEY(name) +); \ No newline at end of file