diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java
index e86327f95262..476f0a85adad 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java
@@ -26,6 +26,7 @@
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
/**
* Populates, initializes, or cleans up a database using SQL scripts defined in
@@ -95,7 +96,7 @@ public ResourceDatabasePopulator(Resource... scripts) {
* @param ignoreFailedDrops flag to indicate that a failed SQL {@code DROP}
* statement can be ignored
* @param sqlScriptEncoding the encoding for the supplied SQL scripts, if
- * different from the platform encoding; may be {@code null}
+ * different from the platform encoding; may be {@code null} or empty
* @param scripts the scripts to execute to initialize or populate the database;
* never {@code null}
* @since 4.0.3
@@ -105,7 +106,7 @@ public ResourceDatabasePopulator(boolean continueOnError, boolean ignoreFailedDr
this(scripts);
this.continueOnError = continueOnError;
this.ignoreFailedDrops = ignoreFailedDrops;
- this.sqlScriptEncoding = sqlScriptEncoding;
+ setSqlScriptEncoding(sqlScriptEncoding);
}
/**
@@ -143,7 +144,7 @@ public void setScripts(Resource... scripts) {
* @see #addScript(Resource)
*/
public void setSqlScriptEncoding(String sqlScriptEncoding) {
- this.sqlScriptEncoding = sqlScriptEncoding;
+ this.sqlScriptEncoding = StringUtils.hasText(sqlScriptEncoding) ? sqlScriptEncoding : null;
}
/**
diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/DatabaseInitializer.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/DatabaseInitializer.java
new file mode 100644
index 000000000000..5505eed8340e
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/DatabaseInitializer.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.springframework.jdbc.datasource.init.ScriptUtils;
+import org.springframework.util.ResourceUtils;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+/**
+ * {@code @DatabaseInitializer} is used to annotate a test class or test method
+ * to configure SQL scripts to be executed against a given database during
+ * integration tests.
+ *
+ *
Method-level declarations override class-level declarations.
+ *
+ *
Script execution is performed by the {@link DatabaseInitializerTestExecutionListener},
+ * which is enabled by default.
+ *
+ *
The configuration options provided by this annotation are a superset of
+ * those provided by the {@code } XML namespace
+ * element. Consult the Javadoc of individual attributes for details.
+ *
+ *
Beginning with Java 8, {@code @DatabaseInitializer} can be used as a
+ * {@linkplain Repeatable repeatable} annotation. Otherwise,
+ * {@link DatabaseInitializers @DatabaseInitializers} can be used as an explicit
+ * container for declaring multiple instances of {@code @DatabaseInitializer}.
+ *
+ *
This annotation may be used as a meta-annotation to create custom
+ * composed annotations; however, attribute overrides are not currently
+ * supported for {@linkplain Repeatable repeatable} annotations that are used as
+ * meta-annotations.
+ *
+ * @author Sam Brannen
+ * @author Tadaya Tsuyukubo
+ * @since 4.1
+ * @see DatabaseInitializers
+ * @see DatabaseInitializerTestExecutionListener
+ * @see org.springframework.transaction.annotation.Transactional
+ * @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
+ * @see org.springframework.jdbc.datasource.init.ResourceDatabasePopulator
+ * @see org.springframework.jdbc.datasource.init.ScriptUtils
+ */
+@Documented
+@Inherited
+@Retention(RUNTIME)
+@Target({ TYPE, METHOD })
+@Repeatable(DatabaseInitializers.class)
+public @interface DatabaseInitializer {
+
+ /**
+ * Enumeration of phases that dictate when SQL scripts are executed.
+ */
+ static enum ExecutionPhase {
+
+ /**
+ * The configured SQL scripts will be executed before the
+ * corresponding test method.
+ */
+ BEFORE_TEST_METHOD,
+
+ /**
+ * The configured SQL scripts will be executed after the
+ * corresponding test method.
+ */
+ AFTER_TEST_METHOD;
+ }
+
+
+ /**
+ * Alias for {@link #scripts}.
+ *
This attribute may not be used in conjunction with
+ * {@link #scripts}, but it may be used instead of {@link #scripts}.
+ */
+ String[] value() default {};
+
+ /**
+ * The paths to the SQL scripts to execute.
+ *
+ *
This attribute may not be used in conjunction with
+ * {@link #value}, but it may be used instead of {@link #value}.
+ *
+ *
Path Resource Semantics
+ * Each path will be interpreted as a Spring
+ * {@link org.springframework.core.io.Resource Resource}. A plain path
+ * — for example, {@code "schema.sql"} — will be treated as a
+ * classpath resource that is relative to the package in which the
+ * test class is defined. A path starting with a slash will be treated as an
+ * absolute classpath resource, for example:
+ * {@code "/org/example/schema.sql"}. A path which references a
+ * URL (e.g., a path prefixed with
+ * {@link ResourceUtils#CLASSPATH_URL_PREFIX classpath:},
+ * {@link ResourceUtils#FILE_URL_PREFIX file:}, {@code http:}, etc.) will be
+ * loaded using the specified resource protocol.
+ *
+ *
Default Script Detection
+ * If no SQL scripts are specified, an attempt will be made to detect a
+ * default script depending on where this annotation is declared.
+ * If a default cannot be detected, an {@link IllegalStateException} will be
+ * thrown.
+ *
+ * - class-level declaration: if the annotated test class
+ * is {@code com.example.MyTest}, the corresponding default script is
+ * {@code "classpath:com/example/MyTest.sql"}.
+ * - method-level declaration: if the annotated test
+ * method is named {@code testMethod()} and is defined in the class
+ * {@code com.example.MyTest}, the corresponding default script is
+ * {@code "classpath:com/example/MyTest.testMethod.sql"}.
+ *
+ */
+ String[] scripts() default {};
+
+ /**
+ * The encoding for the supplied SQL scripts, if different from the platform
+ * encoding.
+ * An empty string denotes that the platform encoding should be used.
+ */
+ String encoding() default "";
+
+ /**
+ * The bean name of the {@link javax.sql.DataSource} against which the scripts
+ * should be executed.
+ *
The name is only used if there is more than one bean of type
+ * {@code DataSource} in the test's {@code ApplicationContext}. If there is
+ * only one such bean, it is not necessary to specify a bean name.
+ *
Defaults to an empty string, requiring that one of the following is
+ * true:
+ *
+ * - There is only one bean of type {@code DataSource} in the test's
+ * {@code ApplicationContext}.
+ * - The {@code DataSource} to use is named {@code "dataSource"}.
+ *
+ */
+ String dataSource() default "";
+
+ /**
+ * The bean name of the {@link org.springframework.transaction.PlatformTransactionManager
+ * PlatformTransactionManager} that should be used to drive transactions.
+ * The name is only used if there is more than one bean of type
+ * {@code PlatformTransactionManager} in the test's {@code ApplicationContext}.
+ * If there is only one such bean, it is not necessary to specify a bean name.
+ *
Defaults to an empty string, requiring that one of the following is
+ * true:
+ *
+ * - There is only one bean of type {@code PlatformTransactionManager} in
+ * the test's {@code ApplicationContext}.
+ * - {@link org.springframework.transaction.annotation.TransactionManagementConfigurer
+ * TransactionManagementConfigurer} has been implemented to specify which
+ * {@code PlatformTransactionManager} bean should be used for annotation-driven
+ * transaction management.
+ * - The {@code PlatformTransactionManager} to use is named
+ * {@code "transactionManager"}.
+ *
+ */
+ String transactionManager() default "";
+
+ /**
+ * Flag to indicate that the SQL scripts must be executed in a new transaction.
+ * Defaults to {@code false}, meaning that the SQL scripts will be executed
+ * within the current transaction if present. The current transaction
+ * will typically be managed by the
+ * {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener
+ * TransactionalTestExecutionListener}.
+ *
Can be set to {@code true} to ensure that the scripts are executed in
+ * a new, isolated transaction that will be immediately committed.
+ */
+ boolean requireNewTransaction() default false;
+
+ /**
+ * The character string used to separate individual statements within the
+ * SQL scripts.
+ *
Defaults to {@code ";"} if not specified and falls back to {@code "\n"}
+ * as a last resort; may be set to {@link ScriptUtils#EOF_STATEMENT_SEPARATOR}
+ * to signal that each script contains a single statement without a separator.
+ */
+ String separator() default ScriptUtils.DEFAULT_STATEMENT_SEPARATOR;
+
+ /**
+ * The prefix that identifies single-line comments within the SQL scripts.
+ *
Defaults to {@code "--"}.
+ */
+ String commentPrefix() default ScriptUtils.DEFAULT_COMMENT_PREFIX;
+
+ /**
+ * The start delimiter that identifies block comments within the SQL scripts.
+ *
Defaults to {@code "/*"}.
+ * @see #blockCommentEndDelimiter
+ */
+ String blockCommentStartDelimiter() default ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER;
+
+ /**
+ * The end delimiter that identifies block comments within the SQL scripts.
+ *
Defaults to "*/".
+ * @see #blockCommentStartDelimiter
+ */
+ String blockCommentEndDelimiter() default ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER;
+
+ /**
+ * Flag to indicate that all failures in SQL should be logged but not cause
+ * a failure.
+ *
Defaults to {@code false}.
+ * @see #ignoreFailedDrops
+ */
+ boolean continueOnError() default false;
+
+ /**
+ * Flag to indicate that a failed SQL {@code DROP} statement can be ignored.
+ *
This is useful for a non-embedded database whose SQL dialect does not
+ * support an {@code IF EXISTS} clause in a {@code DROP} statement.
+ *
The default is {@code false} so that if a script is accidentally
+ * executed, it will fail fast if the script starts with a {@code DROP}
+ * statement.
+ * @see #continueOnError
+ */
+ boolean ignoreFailedDrops() default false;
+
+ /**
+ * When the SQL scripts should be executed.
+ *
Defaults to {@link ExecutionPhase#BEFORE_TEST_METHOD BEFORE_TEST_METHOD}.
+ */
+ ExecutionPhase executionPhase() default ExecutionPhase.BEFORE_TEST_METHOD;
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/DatabaseInitializerTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/DatabaseInitializerTestExecutionListener.java
new file mode 100644
index 000000000000..49b4922096f3
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/DatabaseInitializerTestExecutionListener.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import javax.sql.DataSource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
+import org.springframework.test.context.TestContext;
+import org.springframework.test.context.jdbc.DatabaseInitializer.ExecutionPhase;
+import org.springframework.test.context.support.AbstractTestExecutionListener;
+import org.springframework.test.context.transaction.TestContextTransactionUtils;
+import org.springframework.test.context.util.TestContextResourceUtils;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
+import org.springframework.transaction.interceptor.TransactionAttribute;
+import org.springframework.transaction.support.TransactionCallbackWithoutResult;
+import org.springframework.transaction.support.TransactionTemplate;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.ResourceUtils;
+
+/**
+ * {@code TestExecutionListener} that provides support for executing SQL scripts
+ * configured via the {@link DatabaseInitializer @DatabaseInitializer} annotation.
+ *
+ *
SQL scripts will be executed {@linkplain #beforeTestMethod(TestContext) before}
+ * or {@linkplain #afterTestMethod(TestContext) after} execution of the corresponding
+ * {@linkplain java.lang.reflect.Method test method}, depending on the configured
+ * value of the {@link DatabaseInitializer#requireNewTransaction requireNewTransaction}
+ * flag.
+ *
+ *
Script Resources
+ * For details on default script detection and how explicit script locations
+ * are interpreted, see {@link DatabaseInitializer#scripts}.
+ *
+ *
Required Spring Beans
+ * A {@link DataSource} and {@link PlatformTransactionManager} must be defined
+ * as beans in the Spring {@link ApplicationContext} for the corresponding test.
+ * Consult the Javadoc for {@link TestContextTransactionUtils#retrieveDataSource}
+ * and {@link TestContextTransactionUtils#retrieveTransactionManager} for details
+ * on the algorithms used to locate these beans.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ * @see DatabaseInitializer
+ * @see DatabaseInitializers
+ * @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
+ * @see org.springframework.jdbc.datasource.init.ResourceDatabasePopulator
+ * @see org.springframework.jdbc.datasource.init.ScriptUtils
+ */
+public class DatabaseInitializerTestExecutionListener extends AbstractTestExecutionListener {
+
+ private static final Log logger = LogFactory.getLog(DatabaseInitializerTestExecutionListener.class);
+
+
+ /**
+ * Execute SQL scripts configured via {@link DatabaseInitializer @DatabaseInitializer}
+ * for the supplied {@link TestContext} before the current test method.
+ */
+ @Override
+ public void beforeTestMethod(TestContext testContext) throws Exception {
+ executeDatabaseInitializers(testContext, ExecutionPhase.BEFORE_TEST_METHOD);
+ }
+
+ /**
+ * Execute SQL scripts configured via {@link DatabaseInitializer @DatabaseInitializer}
+ * for the supplied {@link TestContext} after the current test method.
+ */
+ @Override
+ public void afterTestMethod(TestContext testContext) throws Exception {
+ executeDatabaseInitializers(testContext, ExecutionPhase.AFTER_TEST_METHOD);
+ }
+
+ /**
+ * Execute SQL scripts configured via {@link DatabaseInitializer @DatabaseInitializer}
+ * for the supplied {@link TestContext} and {@link ExecutionPhase}.
+ */
+ private void executeDatabaseInitializers(TestContext testContext, ExecutionPhase executionPhase) throws Exception {
+ boolean classLevel = false;
+
+ Set databaseInitializers = AnnotationUtils.getRepeatableAnnotation(
+ testContext.getTestMethod(), DatabaseInitializers.class, DatabaseInitializer.class);
+ if (databaseInitializers.isEmpty()) {
+ databaseInitializers = AnnotationUtils.getRepeatableAnnotation(testContext.getTestClass(),
+ DatabaseInitializers.class, DatabaseInitializer.class);
+ if (!databaseInitializers.isEmpty()) {
+ classLevel = true;
+ }
+ }
+
+ for (DatabaseInitializer databaseInitializer : databaseInitializers) {
+ executeDatabaseInitializer(databaseInitializer, executionPhase, testContext, classLevel);
+ }
+ }
+
+ /**
+ * Execute the SQL scripts configured via the supplied
+ * {@link DatabaseInitializer @DatabaseInitializer} for the given
+ * {@link ExecutionPhase} and {@link TestContext}.
+ *
+ * Special care must be taken in order to properly support the
+ * {@link DatabaseInitializer#requireNewTransaction requireNewTransaction}
+ * flag.
+ *
+ * @param databaseInitializer the {@code @DatabaseInitializer} to parse
+ * @param executionPhase the current execution phase
+ * @param testContext the current {@code TestContext}
+ * @param classLevel {@code true} if {@link DatabaseInitializer @DatabaseInitializer}
+ * was declared at the class level
+ */
+ @SuppressWarnings("serial")
+ private void executeDatabaseInitializer(DatabaseInitializer databaseInitializer, ExecutionPhase executionPhase,
+ TestContext testContext, boolean classLevel) throws Exception {
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Processing %s for execution phase [%s] and test context %s.",
+ databaseInitializer, executionPhase, testContext));
+ }
+
+ if (executionPhase != databaseInitializer.executionPhase()) {
+ return;
+ }
+
+ final ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
+ populator.setSqlScriptEncoding(databaseInitializer.encoding());
+ populator.setSeparator(databaseInitializer.separator());
+ populator.setCommentPrefix(databaseInitializer.commentPrefix());
+ populator.setBlockCommentStartDelimiter(databaseInitializer.blockCommentStartDelimiter());
+ populator.setBlockCommentEndDelimiter(databaseInitializer.blockCommentEndDelimiter());
+ populator.setContinueOnError(databaseInitializer.continueOnError());
+ populator.setIgnoreFailedDrops(databaseInitializer.ignoreFailedDrops());
+
+ String[] scripts = getScripts(databaseInitializer, testContext, classLevel);
+ scripts = TestContextResourceUtils.convertToClasspathResourcePaths(testContext.getTestClass(), scripts);
+ populator.setScripts(TestContextResourceUtils.convertToResources(testContext.getApplicationContext(), scripts));
+ if (logger.isDebugEnabled()) {
+ logger.debug("Executing SQL scripts: " + ObjectUtils.nullSafeToString(scripts));
+ }
+
+ final DataSource dataSource = TestContextTransactionUtils.retrieveDataSource(testContext,
+ databaseInitializer.dataSource());
+ final PlatformTransactionManager transactionManager = TestContextTransactionUtils.retrieveTransactionManager(
+ testContext, databaseInitializer.transactionManager());
+
+ int propagation = databaseInitializer.requireNewTransaction() ? TransactionDefinition.PROPAGATION_REQUIRES_NEW
+ : TransactionDefinition.PROPAGATION_REQUIRED;
+
+ TransactionAttribute transactionAttribute = TestContextTransactionUtils.createDelegatingTransactionAttribute(
+ testContext, new DefaultTransactionAttribute(propagation));
+
+ new TransactionTemplate(transactionManager, transactionAttribute).execute(new TransactionCallbackWithoutResult() {
+
+ @Override
+ public void doInTransactionWithoutResult(TransactionStatus status) {
+ populator.execute(dataSource);
+ };
+ });
+ }
+
+ private String[] getScripts(DatabaseInitializer databaseInitializer, TestContext testContext, boolean classLevel) {
+ String[] scripts = databaseInitializer.scripts();
+ String[] value = databaseInitializer.value();
+ boolean scriptsDeclared = !ObjectUtils.isEmpty(scripts);
+ boolean valueDeclared = !ObjectUtils.isEmpty(value);
+
+ if (valueDeclared && scriptsDeclared) {
+ String elementType = (classLevel ? "class" : "method");
+ String elementName = (classLevel ? testContext.getTestClass().getName()
+ : testContext.getTestMethod().toString());
+ String msg = String.format("Test %s [%s] has been configured with @DatabaseInitializer's 'value' [%s] "
+ + "and 'scripts' [%s] attributes. Only one declaration of SQL script "
+ + "paths is permitted per @DatabaseInitializer annotation.", elementType, elementName,
+ ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(scripts));
+ logger.error(msg);
+ throw new IllegalStateException(msg);
+ }
+ if (valueDeclared) {
+ scripts = value;
+ }
+ if (ObjectUtils.isEmpty(scripts)) {
+ scripts = new String[] { detectDefaultScript(testContext, classLevel) };
+ }
+ return scripts;
+ }
+
+ /**
+ * Detect a default SQL script by implementing the algorithm defined in
+ * {@link DatabaseInitializer#scripts}.
+ */
+ private String detectDefaultScript(TestContext testContext, boolean classLevel) {
+ Class> clazz = testContext.getTestClass();
+ Method method = testContext.getTestMethod();
+ String elementType = (classLevel ? "class" : "method");
+ String elementName = (classLevel ? clazz.getName() : method.toString());
+
+ String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName());
+ if (!classLevel) {
+ resourcePath += "." + method.getName();
+ }
+ resourcePath += ".sql";
+
+ String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath;
+ ClassPathResource classPathResource = new ClassPathResource(resourcePath);
+
+ if (classPathResource.exists()) {
+ if (logger.isInfoEnabled()) {
+ logger.info(String.format("Detected default SQL script \"%s\" for test %s [%s]", prefixedResourcePath,
+ elementType, elementName));
+ }
+ return prefixedResourcePath;
+ }
+ else {
+ String msg = String.format("Could not detect default SQL script for test %s [%s]: "
+ + "%s does not exist. Either declare scripts via @DatabaseInitializer or make the "
+ + "default SQL script available.", elementType, elementName, classPathResource);
+ logger.error(msg);
+ throw new IllegalStateException(msg);
+ }
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/DatabaseInitializers.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/DatabaseInitializers.java
new file mode 100644
index 000000000000..accb495bec41
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/DatabaseInitializers.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+/**
+ * Container annotation that aggregates several {@link DatabaseInitializer}
+ * annotations.
+ *
+ *
Can be used natively, declaring several nested {@link DatabaseInitializer}
+ * annotations. Can also be used in conjunction with Java 8's support for
+ * repeatable annotations, where {@code @DatabaseInitializer} can simply be
+ * declared several times on the same class or method, implicitly generating
+ * this container annotation.
+ *
+ *
This annotation may be used as a meta-annotation to create custom
+ * composed annotations.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ * @see DatabaseInitializer
+ */
+@Documented
+@Inherited
+@Retention(RUNTIME)
+@Target({ TYPE, METHOD })
+public @interface DatabaseInitializers {
+
+ DatabaseInitializer[] value();
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java
index da5bbb02796b..046a702cdc6b 100644
--- a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java
@@ -26,6 +26,7 @@
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.jdbc.DatabaseInitializerTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.test.jdbc.JdbcTestUtils;
import org.springframework.transaction.PlatformTransactionManager;
@@ -63,6 +64,7 @@
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.TestExecutionListeners
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
+ * @see org.springframework.test.context.jdbc.DatabaseInitializerTestExecutionListener
* @see org.springframework.test.context.transaction.TransactionConfiguration
* @see org.springframework.transaction.annotation.Transactional
* @see org.springframework.test.annotation.Rollback
@@ -71,7 +73,7 @@
* @see org.springframework.test.jdbc.JdbcTestUtils
* @see org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests
*/
-@TestExecutionListeners(TransactionalTestExecutionListener.class)
+@TestExecutionListeners({ TransactionalTestExecutionListener.class, DatabaseInitializerTestExecutionListener.class })
@Transactional
public abstract class AbstractTransactionalJUnit4SpringContextTests extends AbstractJUnit4SpringContextTests {
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java
index 4bfd00655d9a..e21f4e5e7562 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2014 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.
@@ -22,7 +22,6 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
@@ -30,16 +29,15 @@
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.SmartContextLoader;
+import org.springframework.test.context.util.TestContextResourceUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ResourceUtils;
-import org.springframework.util.StringUtils;
/**
* Abstract application context loader that provides a basis for all concrete
@@ -63,8 +61,6 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
- private static final String SLASH = "/";
-
private static final Log logger = LogFactory.getLog(AbstractContextLoader.class);
@@ -87,7 +83,8 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
*/
@Override
public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
- String[] processedLocations = processLocations(configAttributes.getDeclaringClass(), configAttributes.getLocations());
+ String[] processedLocations = processLocations(configAttributes.getDeclaringClass(),
+ configAttributes.getLocations());
configAttributes.setLocations(processedLocations);
}
@@ -118,25 +115,23 @@ public void processContextConfiguration(ContextConfigurationAttributes configAtt
protected void prepareContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
context.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
- Set>> initializerClasses =
- mergedConfig.getContextInitializerClasses();
+ Set>> initializerClasses = mergedConfig.getContextInitializerClasses();
if (initializerClasses.isEmpty()) {
// no ApplicationContextInitializers have been declared -> nothing to do
return;
}
- List> initializerInstances =
- new ArrayList>();
+ List> initializerInstances = new ArrayList>();
Class> contextClass = context.getClass();
for (Class extends ApplicationContextInitializer extends ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass,
- ApplicationContextInitializer.class);
+ ApplicationContextInitializer.class);
Assert.isAssignable(initializerContextClass, contextClass, String.format(
- "Could not add context initializer [%s] since its generic parameter [%s] " +
- "is not assignable from the type of application context used by this " +
- "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
- contextClass.getName()));
+ "Could not add context initializer [%s] since its generic parameter [%s] "
+ + "is not assignable from the type of application context used by this "
+ + "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
+ contextClass.getName()));
initializerInstances.add((ApplicationContextInitializer) BeanUtils.instantiateClass(initializerClass));
}
@@ -171,8 +166,8 @@ protected void prepareContext(ConfigurableApplicationContext context, MergedCont
*/
@Override
public final String[] processLocations(Class> clazz, String... locations) {
- return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ?
- generateDefaultLocations(clazz) : modifyLocations(clazz, locations);
+ return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ? generateDefaultLocations(clazz)
+ : modifyLocations(clazz, locations);
}
/**
@@ -180,7 +175,7 @@ public final String[] processLocations(Class> clazz, String... locations) {
* supplied class.
* For example, if the supplied class is {@code com.example.MyTest},
* the generated locations will contain a single string with a value of
- * "classpath:/com/example/MyTest{@code }",
+ * "classpath:com/example/MyTest{@code }",
* where {@code } is the value of the
* {@link #getResourceSuffix() resource suffix} string.
* As of Spring 3.1, the implementation of this method adheres to the
@@ -199,21 +194,21 @@ protected String[] generateDefaultLocations(Class> clazz) {
Assert.notNull(clazz, "Class must not be null");
String suffix = getResourceSuffix();
Assert.hasText(suffix, "Resource suffix must not be empty");
- String resourcePath = SLASH + ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix;
+ String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix;
String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath;
- ClassPathResource classPathResource = new ClassPathResource(resourcePath, clazz);
+ ClassPathResource classPathResource = new ClassPathResource(resourcePath);
if (classPathResource.exists()) {
if (logger.isInfoEnabled()) {
logger.info(String.format("Detected default resource location \"%s\" for test class [%s]",
- prefixedResourcePath, clazz.getName()));
+ prefixedResourcePath, clazz.getName()));
}
- return new String[] {prefixedResourcePath};
+ return new String[] { prefixedResourcePath };
}
else {
if (logger.isInfoEnabled()) {
- logger.info(String.format("Could not detect default resource locations for test class [%s]: " +
- "%s does not exist", clazz.getName(), classPathResource));
+ logger.info(String.format("Could not detect default resource locations for test class [%s]: "
+ + "%s does not exist", clazz.getName(), classPathResource));
}
return EMPTY_STRING_ARRAY;
}
@@ -221,15 +216,8 @@ protected String[] generateDefaultLocations(Class> clazz) {
/**
* Generate a modified version of the supplied locations array and return it.
- *
A plain path — for example, "context.xml" — will
- * be treated as a classpath resource that is relative to the package in which
- * the specified class is defined. A path starting with a slash is treated
- * as an absolute classpath location, for example:
- * "/org/springframework/whatever/foo.xml". A path which
- * references a URL (e.g., a path prefixed with
- * {@link ResourceUtils#CLASSPATH_URL_PREFIX classpath:},
- * {@link ResourceUtils#FILE_URL_PREFIX file:}, {@code http:},
- * etc.) will be added to the results unchanged.
+ *
The default implementation simply delegates to
+ * {@link TestContextResourceUtils#convertToClasspathResourcePaths}.
*
Subclasses can override this method to implement a different
* location modification strategy.
* @param clazz the class with which the locations are associated
@@ -238,21 +226,7 @@ protected String[] generateDefaultLocations(Class> clazz) {
* @since 2.5
*/
protected String[] modifyLocations(Class> clazz, String... locations) {
- String[] modifiedLocations = new String[locations.length];
- for (int i = 0; i < locations.length; i++) {
- String path = locations[i];
- if (path.startsWith(SLASH)) {
- modifiedLocations[i] = ResourceUtils.CLASSPATH_URL_PREFIX + path;
- }
- else if (!ResourcePatternUtils.isUrl(path)) {
- modifiedLocations[i] = ResourceUtils.CLASSPATH_URL_PREFIX + SLASH +
- StringUtils.cleanPath(ClassUtils.classPackageAsResourcePath(clazz) + SLASH + path);
- }
- else {
- modifiedLocations[i] = StringUtils.cleanPath(path);
- }
- }
- return modifiedLocations;
+ return TestContextResourceUtils.convertToClasspathResourcePaths(clazz, locations);
}
/**
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContextBootstrapper.java
index d3cf3f877621..1d3c9226a219 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContextBootstrapper.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContextBootstrapper.java
@@ -38,6 +38,7 @@
*
{@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener}
* {@link org.springframework.test.context.support.DirtiesContextTestExecutionListener}
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener}
+ * {@link org.springframework.test.context.jdbc.DatabaseInitializerTestExecutionListener}
*
* Uses {@link DelegatingSmartContextLoader} as the default {@link ContextLoader}.
* Builds a standard {@link MergedContextConfiguration}.
@@ -51,7 +52,8 @@ public class DefaultTestContextBootstrapper extends AbstractTestContextBootstrap
private static final List DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES = Collections.unmodifiableList(Arrays.asList(
"org.springframework.test.context.support.DependencyInjectionTestExecutionListener",
"org.springframework.test.context.support.DirtiesContextTestExecutionListener",
- "org.springframework.test.context.transaction.TransactionalTestExecutionListener"));
+ "org.springframework.test.context.transaction.TransactionalTestExecutionListener",
+ "org.springframework.test.context.jdbc.DatabaseInitializerTestExecutionListener"));
/**
@@ -61,6 +63,7 @@ public class DefaultTestContextBootstrapper extends AbstractTestContextBootstrap
* {@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener}
* {@link org.springframework.test.context.support.DirtiesContextTestExecutionListener}
* {@link org.springframework.test.context.transaction.TransactionalTestExecutionListener}
+ * {@link org.springframework.test.context.jdbc.DatabaseInitializerTestExecutionListener}
*
*/
protected List getDefaultTestExecutionListenerClassNames() {
diff --git a/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java b/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java
index 4228e06aadaa..56ed682718e6 100644
--- a/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java
+++ b/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java
@@ -25,6 +25,7 @@
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.jdbc.DatabaseInitializerTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.test.jdbc.JdbcTestUtils;
import org.springframework.transaction.PlatformTransactionManager;
@@ -54,6 +55,7 @@
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.TestExecutionListeners
* @see org.springframework.test.context.transaction.TransactionalTestExecutionListener
+ * @see org.springframework.test.context.jdbc.DatabaseInitializerTestExecutionListener
* @see org.springframework.test.context.transaction.TransactionConfiguration
* @see org.springframework.transaction.annotation.Transactional
* @see org.springframework.test.annotation.Rollback
@@ -62,7 +64,7 @@
* @see org.springframework.test.jdbc.JdbcTestUtils
* @see org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests
*/
-@TestExecutionListeners(TransactionalTestExecutionListener.class)
+@TestExecutionListeners({ TransactionalTestExecutionListener.class, DatabaseInitializerTestExecutionListener.class })
@Transactional
public abstract class AbstractTransactionalTestNGSpringContextTests extends AbstractTestNGSpringContextTests {
diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java
new file mode 100644
index 000000000000..0bd3f462bd76
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TestContextTransactionUtils.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2002-2014 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.transaction;
+
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.test.context.TestContext;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.TransactionManagementConfigurer;
+import org.springframework.transaction.interceptor.DelegatingTransactionAttribute;
+import org.springframework.transaction.interceptor.TransactionAttribute;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Utility methods for working with transactions and data access related beans
+ * within the Spring TestContext Framework. Mainly for internal use
+ * within the framework.
+ *
+ * @author Sam Brannen
+ * @author Juergen Hoeller
+ * @since 4.1
+ */
+public abstract class TestContextTransactionUtils {
+
+ private static final Log logger = LogFactory.getLog(TestContextTransactionUtils.class);
+
+ /**
+ * Default bean name for a {@link DataSource}: {@code "dataSource"}.
+ */
+ public static final String DEFAULT_DATA_SOURCE_NAME = "dataSource";
+
+ /**
+ * Default bean name for a {@link PlatformTransactionManager}:
+ * {@code "transactionManager"}.
+ */
+ public static final String DEFAULT_TRANSACTION_MANAGER_NAME = "transactionManager";
+
+
+ private TestContextTransactionUtils() {
+ /* prevent instantiation */
+ }
+
+ /**
+ * Retrieve the {@link DataSource} to use for the supplied {@linkplain TestContext
+ * test context}.
+ * The following algorithm is used to retrieve the {@code DataSource}
+ * from the {@link org.springframework.context.ApplicationContext ApplicationContext}
+ * of the supplied test context:
+ *
+ * - Look up the {@code DataSource} by type and name, if the supplied
+ * {@code name} is non-empty.
+ *
- Look up the {@code DataSource} by type.
+ *
- Look up the {@code DataSource} by type and the
+ * {@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 empty
+ * @return the {@code DataSource} to use, or {@code null} if not found
+ * @throws BeansException if an error occurs while retrieving the {@code DataSource}
+ */
+ public static DataSource retrieveDataSource(TestContext testContext, String name) {
+ Assert.notNull(testContext, "TestContext must not be null");
+ BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
+
+ try {
+ // look up by type and explicit name
+ if (StringUtils.hasText(name)) {
+ return bf.getBean(name, DataSource.class);
+ }
+
+ if (bf instanceof ListableBeanFactory) {
+ ListableBeanFactory lbf = (ListableBeanFactory) bf;
+
+ // look up single bean by type
+ Map dataSources = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf,
+ DataSource.class);
+ if (dataSources.size() == 1) {
+ return dataSources.values().iterator().next();
+ }
+ }
+
+ // look up by type and default name
+ return bf.getBean(DEFAULT_DATA_SOURCE_NAME, DataSource.class);
+ }
+ catch (BeansException ex) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Caught exception while retrieving DataSource for test context " + testContext, ex);
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Retrieve the {@linkplain PlatformTransactionManager transaction manager}
+ * to use for the supplied {@linkplain TestContext test context}.
+ *
The following algorithm is used to retrieve the transaction manager
+ * from the {@link org.springframework.context.ApplicationContext ApplicationContext}
+ * of the supplied test context:
+ *
+ * - Look up the transaction manager by type and name, if the supplied
+ * {@code name} is non-empty.
+ *
- Look up the transaction manager by type.
+ *
- Look up the transaction manager via a {@link TransactionManagementConfigurer},
+ * if present.
+ *
- Look up the transaction manager by type and the
+ * {@linkplain #DEFAULT_TRANSACTION_MANAGER_NAME default transaction manager
+ * 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 empty
+ * @return the transaction manager to use, or {@code null} if not found
+ * @throws BeansException if an error occurs while retrieving the transaction manager
+ */
+ public static PlatformTransactionManager retrieveTransactionManager(TestContext testContext, String name) {
+ Assert.notNull(testContext, "TestContext must not be null");
+ BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
+
+ try {
+ // look up by type and explicit name
+ if (StringUtils.hasText(name)) {
+ return bf.getBean(name, PlatformTransactionManager.class);
+ }
+
+ if (bf instanceof ListableBeanFactory) {
+ ListableBeanFactory lbf = (ListableBeanFactory) bf;
+
+ // look up single bean by type
+ Map txMgrs = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf,
+ PlatformTransactionManager.class);
+ if (txMgrs.size() == 1) {
+ return txMgrs.values().iterator().next();
+ }
+
+ // look up single TransactionManagementConfigurer
+ Map configurers = BeanFactoryUtils.beansOfTypeIncludingAncestors(
+ lbf, TransactionManagementConfigurer.class);
+ if (configurers.size() > 1) {
+ throw new IllegalStateException(
+ "Only one TransactionManagementConfigurer may exist in the ApplicationContext");
+ }
+ if (configurers.size() == 1) {
+ return configurers.values().iterator().next().annotationDrivenTransactionManager();
+ }
+ }
+
+ // look up by type and default name
+ return bf.getBean(DEFAULT_TRANSACTION_MANAGER_NAME, PlatformTransactionManager.class);
+ }
+ catch (BeansException ex) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Caught exception while retrieving transaction manager for test context " + testContext, ex);
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Create a delegating {@link TransactionAttribute} for the supplied target
+ * {@link TransactionAttribute} and {@link TestContext}, using the names of
+ * the test class and test method as 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}
+ * @return the delegating {@code TransactionAttribute}
+ */
+ 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);
+ }
+
+
+ @SuppressWarnings("serial")
+ private static class TestContextTransactionAttribute extends DelegatingTransactionAttribute {
+
+ private final String name;
+
+
+ public TestContextTransactionAttribute(TransactionAttribute targetAttribute, TestContext testContext) {
+ super(targetAttribute);
+ this.name = testContext.getTestClass().getName() + "." + testContext.getTestMethod().getName();
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java
index 3fc2759360d7..a00106c44fed 100644
--- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java
+++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2014 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.
@@ -45,17 +45,22 @@
* The bean name of the {@link org.springframework.transaction.PlatformTransactionManager
* PlatformTransactionManager} that should be used to drive transactions.
*
- *
This attribute is not required and only needs to be declared if there
- * are multiple beans of type {@code PlatformTransactionManager} in the test's
- * {@code ApplicationContext} and if one of the following is true.
- *
- * - the bean name of the desired {@code PlatformTransactionManager} is not
- * "transactionManager"
+ * The name is only used if there is more than one bean of type
+ * {@code PlatformTransactionManager} in the test's {@code ApplicationContext}.
+ * If there is only one such bean, it is not necessary to specify a bean name.
+ *
+ *
Defaults to an empty string, requiring that one of the following is
+ * true:
+ *
+ * - There is only one bean of type {@code PlatformTransactionManager} in
+ * the test's {@code ApplicationContext}.
* - {@link org.springframework.transaction.annotation.TransactionManagementConfigurer
- * TransactionManagementConfigurer} was not implemented to specify which
+ * TransactionManagementConfigurer} has been implemented to specify which
* {@code PlatformTransactionManager} bean should be used for annotation-driven
- * transaction management
- *
+ * transaction management.
+ * - The {@code PlatformTransactionManager} to use is named
+ * {@code "transactionManager"}.
+ *
*
* NOTE: The XML {@code } element also refers
* to a bean named "transactionManager" by default. If you are using both
@@ -63,7 +68,7 @@
* bean - here in {@code @TransactionConfiguration} and also in
* {@code }.
*/
- String transactionManager() default "transactionManager";
+ String transactionManager() default "";
/**
* Should transactions be rolled back by default?
diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
index 35197a5308ff..8c6cf27280ad 100644
--- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2014 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.
@@ -29,8 +29,6 @@
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
-import org.springframework.beans.factory.BeanFactoryUtils;
-import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
@@ -44,7 +42,6 @@
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
-import org.springframework.transaction.interceptor.DelegatingTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.util.Assert;
@@ -126,10 +123,10 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
* @see org.springframework.transaction.annotation.Transactional
* @see #getTransactionManager(TestContext, String)
*/
- @SuppressWarnings("serial")
@Override
- public void beforeTestMethod(TestContext testContext) throws Exception {
+ public void beforeTestMethod(final TestContext testContext) throws Exception {
final Method testMethod = testContext.getTestMethod();
+ final Class> testClass = testContext.getTestClass();
Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null");
if (this.transactionContextCache.remove(testMethod) != null) {
@@ -138,17 +135,11 @@ public void beforeTestMethod(TestContext testContext) throws Exception {
}
PlatformTransactionManager tm = null;
- TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod,
- testContext.getTestClass());
+ TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod, testClass);
if (transactionAttribute != null) {
- transactionAttribute = new DelegatingTransactionAttribute(transactionAttribute) {
-
- @Override
- public String getName() {
- return testMethod.getName();
- }
- };
+ transactionAttribute = TestContextTransactionUtils.createDelegatingTransactionAttribute(testContext,
+ transactionAttribute);
if (logger.isDebugEnabled()) {
logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context "
@@ -302,7 +293,7 @@ private void endTransaction(TestContext testContext, TransactionContext txContex
/**
* Get the {@link PlatformTransactionManager transaction manager} to use
- * for the supplied {@link TestContext test context} and {@code qualifier}.
+ * for the supplied {@linkplain TestContext test context} and {@code qualifier}.
* Delegates to {@link #getTransactionManager(TestContext)} if the
* supplied {@code qualifier} is {@code null} or empty.
* @param testContext the test context for which the transaction manager
@@ -340,6 +331,8 @@ protected PlatformTransactionManager getTransactionManager(TestContext testConte
/**
* Get the {@link PlatformTransactionManager transaction manager} to use
* for the supplied {@link TestContext test context}.
+ *
The default implementation simply delegates to
+ * {@link TestContextTransactionUtils#retrieveTransactionManager}.
* @param testContext the test context for which the transaction manager
* should be retrieved
* @return the transaction manager to use, or {@code null} if not found
@@ -347,47 +340,8 @@ protected PlatformTransactionManager getTransactionManager(TestContext testConte
* @see #getTransactionManager(TestContext, String)
*/
protected PlatformTransactionManager getTransactionManager(TestContext testContext) {
- BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();
String tmName = retrieveConfigurationAttributes(testContext).getTransactionManagerName();
-
- try {
- // look up by type and explicit name from @TransactionConfiguration
- if (StringUtils.hasText(tmName) && !DEFAULT_TRANSACTION_MANAGER_NAME.equals(tmName)) {
- return bf.getBean(tmName, PlatformTransactionManager.class);
- }
-
- if (bf instanceof ListableBeanFactory) {
- ListableBeanFactory lbf = (ListableBeanFactory) bf;
-
- // look up single bean by type
- Map txMgrs = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf,
- PlatformTransactionManager.class);
- if (txMgrs.size() == 1) {
- return txMgrs.values().iterator().next();
- }
-
- // look up single TransactionManagementConfigurer
- Map configurers = BeanFactoryUtils.beansOfTypeIncludingAncestors(
- lbf, TransactionManagementConfigurer.class);
- if (configurers.size() > 1) {
- throw new IllegalStateException(
- "Only one TransactionManagementConfigurer may exist in the ApplicationContext");
- }
- if (configurers.size() == 1) {
- return configurers.values().iterator().next().annotationDrivenTransactionManager();
- }
- }
-
- // look up by type and default name from @TransactionConfiguration
- return bf.getBean(DEFAULT_TRANSACTION_MANAGER_NAME, PlatformTransactionManager.class);
-
- }
- catch (BeansException ex) {
- if (logger.isWarnEnabled()) {
- logger.warn("Caught exception while retrieving transaction manager for test context " + testContext, ex);
- }
- throw ex;
- }
+ return TestContextTransactionUtils.retrieveTransactionManager(testContext, tmName);
}
/**
diff --git a/spring-test/src/main/java/org/springframework/test/context/util/TestContextResourceUtils.java b/spring-test/src/main/java/org/springframework/test/context/util/TestContextResourceUtils.java
new file mode 100644
index 000000000000..c44fcc27ec87
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/util/TestContextResourceUtils.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2002-2014 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.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.support.ResourcePatternUtils;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ResourceUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Utility methods for working with resources within the Spring TestContext
+ * Framework. Mainly for internal use within the framework.
+ *
+ * @author Sam Brannen
+ * @author Tadaya Tsuyukubo
+ * @since 4.1
+ * @see org.springframework.util.ResourceUtils
+ * @see org.springframework.core.io.Resource
+ * @see org.springframework.core.io.ClassPathResource
+ * @see org.springframework.core.io.FileSystemResource
+ * @see org.springframework.core.io.UrlResource
+ * @see org.springframework.core.io.ResourceLoader
+ */
+public abstract class TestContextResourceUtils {
+
+ private static final String SLASH = "/";
+
+
+ private TestContextResourceUtils() {
+ /* prevent instantiation */
+ }
+
+ /**
+ * Convert the supplied paths to classpath resource paths.
+ *
+ * For each of the supplied paths:
+ *
+ * - A plain path — for example, {@code "context.xml"} — will
+ * be treated as a classpath resource that is relative to the package in
+ * which the specified class is defined.
+ *
- A path starting with a slash will be treated as an absolute path
+ * within the classpath, for example: {@code "/org/example/schema.sql"}.
+ *
- A path which is prefixed with a URL protocol (e.g.,
+ * {@link ResourceUtils#CLASSPATH_URL_PREFIX classpath:},
+ * {@link ResourceUtils#FILE_URL_PREFIX file:}, {@code http:}, etc.) will be
+ * {@link StringUtils#cleanPath cleaned} but otherwise unmodified.
+ *
+ * @param clazz the class with which the paths are associated
+ * @param paths the paths to be converted
+ * @return a new array of converted resource paths
+ * @see #convertToResources
+ */
+ public static String[] convertToClasspathResourcePaths(Class> clazz, String... paths) {
+ String[] convertedPaths = new String[paths.length];
+ for (int i = 0; i < paths.length; i++) {
+ String path = paths[i];
+ if (path.startsWith(SLASH)) {
+ convertedPaths[i] = ResourceUtils.CLASSPATH_URL_PREFIX + path;
+ }
+ else if (!ResourcePatternUtils.isUrl(path)) {
+ convertedPaths[i] = ResourceUtils.CLASSPATH_URL_PREFIX + SLASH
+ + StringUtils.cleanPath(ClassUtils.classPackageAsResourcePath(clazz) + SLASH + path);
+ }
+ else {
+ convertedPaths[i] = StringUtils.cleanPath(path);
+ }
+ }
+ return convertedPaths;
+ }
+
+ /**
+ * Convert the supplied paths to {@link Resource} handles using the given
+ * {@link ResourceLoader}.
+ *
+ * @param resourceLoader the {@code ResourceLoader} to use to convert the paths
+ * @param paths the paths to be converted
+ * @return a new array of resources
+ * @see #convertToClasspathResourcePaths
+ */
+ public static Resource[] convertToResources(ResourceLoader resourceLoader, String... paths) {
+ List list = new ArrayList();
+ for (String path : paths) {
+ list.add(resourceLoader.getResource(path));
+ }
+ return list.toArray(new Resource[list.size()]);
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java
index 5142ee5a3f1a..e8561ebe3cc5 100644
--- a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java
@@ -45,7 +45,7 @@ public class TestExecutionListenersTests {
@Test
public void verifyNumDefaultListenersRegistered() throws Exception {
TestContextManager testContextManager = new TestContextManager(DefaultListenersExampleTestCase.class);
- assertEquals("Num registered TELs for DefaultListenersExampleTestCase.", 3,
+ assertEquals("Num registered TELs for DefaultListenersExampleTestCase.", 4,
testContextManager.getTestExecutionListeners().size());
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/CustomScriptSyntaxDatabaseInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/CustomScriptSyntaxDatabaseInitializerTests.java
new file mode 100644
index 000000000000..3d4b7b0b612f
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/CustomScriptSyntaxDatabaseInitializerTests.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import org.junit.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests that verify support for custom SQL script syntax
+ * configured via {@link DatabaseInitializer @DatabaseInitializer}.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@ContextConfiguration(classes = EmptyDatabaseConfig.class)
+@DirtiesContext
+public class CustomScriptSyntaxDatabaseInitializerTests extends AbstractTransactionalJUnit4SpringContextTests {
+
+ @Test
+ @DatabaseInitializers({//
+ @DatabaseInitializer("schema.sql"),//
+ @DatabaseInitializer(//
+ scripts = "data-add-users-with-custom-script-syntax.sql",//
+ commentPrefix = "`",//
+ blockCommentStartDelimiter = "#$",//
+ blockCommentEndDelimiter = "$#",//
+ separator = "@@"//
+ ) //
+ })
+ public void methodLevelScripts() {
+ assertNumUsers(3);
+ }
+
+ protected void assertNumUsers(int expected) {
+ assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/DatabaseInitializerTestExecutionListenerTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/DatabaseInitializerTestExecutionListenerTests.java
new file mode 100644
index 000000000000..16c2e923a5c2
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/DatabaseInitializerTestExecutionListenerTests.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.test.context.TestContext;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * Unit tests for {@link DatabaseInitializerTestExecutionListener}.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+public class DatabaseInitializerTestExecutionListenerTests {
+
+ private final DatabaseInitializerTestExecutionListener listener = new DatabaseInitializerTestExecutionListener();
+
+ private final TestContext testContext = mock(TestContext.class);
+
+
+ @Test
+ public void missingValueAndScriptsAtClassLevel() throws Exception {
+ Class> clazz = MissingValueAndScriptsAtClassLevel.class;
+ Mockito.> when(testContext.getTestClass()).thenReturn(clazz);
+ when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("foo"));
+
+ assertExceptionContains(clazz.getSimpleName() + ".sql");
+ }
+
+ @Test
+ public void missingValueAndScriptsAtMethodLevel() throws Exception {
+ Class> clazz = MissingValueAndScriptsAtMethodLevel.class;
+ Mockito.> when(testContext.getTestClass()).thenReturn(clazz);
+ when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("foo"));
+
+ assertExceptionContains(clazz.getSimpleName() + ".foo" + ".sql");
+ }
+
+ @Test
+ public void valueAndScriptsDeclared() throws Exception {
+ Class> clazz = ValueAndScriptsDeclared.class;
+ Mockito.> when(testContext.getTestClass()).thenReturn(clazz);
+ when(testContext.getTestMethod()).thenReturn(clazz.getDeclaredMethod("valueAndScriptsDeclared"));
+
+ assertExceptionContains("Only one declaration of SQL script paths is permitted");
+ }
+
+ private void assertExceptionContains(String msg) throws Exception {
+ try {
+ listener.beforeTestMethod(testContext);
+ fail("Should have thrown an IllegalStateException.");
+ }
+ catch (IllegalStateException e) {
+ assertTrue("Exception message should contain: " + msg, e.getMessage().contains(msg));
+ }
+ }
+
+
+ // -------------------------------------------------------------------------
+
+ @DatabaseInitializer
+ static class MissingValueAndScriptsAtClassLevel {
+
+ public void foo() {
+ }
+ }
+
+ static class MissingValueAndScriptsAtMethodLevel {
+
+ @DatabaseInitializer
+ public void foo() {
+ }
+ }
+
+ static class ValueAndScriptsDeclared {
+
+ @DatabaseInitializer(value = "foo", scripts = "bar")
+ public void valueAndScriptsDeclared() {
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/DefaultScriptDetectionDatabaseInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/DefaultScriptDetectionDatabaseInitializerTests.java
new file mode 100644
index 000000000000..86d4fc80e661
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/DefaultScriptDetectionDatabaseInitializerTests.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import org.junit.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests that verify support for default SQL script detection.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@ContextConfiguration(classes = EmptyDatabaseConfig.class)
+@DatabaseInitializer
+@DirtiesContext
+public class DefaultScriptDetectionDatabaseInitializerTests extends AbstractTransactionalJUnit4SpringContextTests {
+
+ @Test
+ public void classLevel() {
+ assertNumUsers(2);
+ }
+
+ @Test
+ @DatabaseInitializer
+ public void methodLevel() {
+ assertNumUsers(3);
+ }
+
+ protected void assertNumUsers(int expected) {
+ assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/DefaultScriptDetectionDatabaseInitializerTests.methodLevel.sql b/spring-test/src/test/java/org/springframework/test/context/jdbc/DefaultScriptDetectionDatabaseInitializerTests.methodLevel.sql
new file mode 100644
index 000000000000..06ad7cdec9a1
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/DefaultScriptDetectionDatabaseInitializerTests.methodLevel.sql
@@ -0,0 +1,10 @@
+DROP TABLE user IF EXISTS;
+
+CREATE TABLE user (
+ name VARCHAR(20) NOT NULL,
+ PRIMARY KEY(name)
+);
+
+INSERT INTO user VALUES('Dilbert');
+INSERT INTO user VALUES('Dogbert');
+INSERT INTO user VALUES('Catbert');
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/DefaultScriptDetectionDatabaseInitializerTests.sql b/spring-test/src/test/java/org/springframework/test/context/jdbc/DefaultScriptDetectionDatabaseInitializerTests.sql
new file mode 100644
index 000000000000..e60b7a4b8a5d
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/DefaultScriptDetectionDatabaseInitializerTests.sql
@@ -0,0 +1,9 @@
+DROP TABLE user IF EXISTS;
+
+CREATE TABLE user (
+ name VARCHAR(20) NOT NULL,
+ PRIMARY KEY(name)
+);
+
+INSERT INTO user VALUES('Dilbert');
+INSERT INTO user VALUES('Dogbert');
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/EmptyDatabaseConfig.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/EmptyDatabaseConfig.java
new file mode 100644
index 000000000000..90d5b34fe706
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/EmptyDatabaseConfig.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import javax.sql.DataSource;
+
+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.transaction.PlatformTransactionManager;
+
+/**
+ * Empty database configuration class for SQL script integration tests.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@Configuration
+public class EmptyDatabaseConfig {
+
+ @Bean
+ public PlatformTransactionManager transactionManager() {
+ return new DataSourceTransactionManager(dataSource());
+ }
+
+ @Bean
+ public DataSource dataSource() {
+ return new EmbeddedDatabaseBuilder().build();
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/MetaAnnotationDatabaseInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/MetaAnnotationDatabaseInitializerTests.java
new file mode 100644
index 000000000000..b309569c34fa
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/MetaAnnotationDatabaseInitializerTests.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.junit.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests that verify support for using
+ * {@link DatabaseInitializer @DatabaseInitializer} and
+ * {@link DatabaseInitializers @DatabaseInitializers} as a meta-annotations.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@ContextConfiguration(classes = EmptyDatabaseConfig.class)
+@DirtiesContext
+public class MetaAnnotationDatabaseInitializerTests extends AbstractTransactionalJUnit4SpringContextTests {
+
+ @Test
+ @MetaDbInitializer
+ public void metaDatabaseInitializer() {
+ assertNumUsers(1);
+ }
+
+ @Test
+ @MetaDbInitializers
+ public void metaDatabaseInitializers() {
+ assertNumUsers(1);
+ }
+
+ protected void assertNumUsers(int expected) {
+ assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
+ }
+
+
+ @DatabaseInitializer({ "drop-schema.sql", "schema.sql", "data.sql" })
+ @Retention(RUNTIME)
+ @Target(METHOD)
+ static @interface MetaDbInitializer {
+ }
+
+ @DatabaseInitializers({//
+ @DatabaseInitializer("drop-schema.sql"),//
+ @DatabaseInitializer("schema.sql"),//
+ @DatabaseInitializer("data.sql") //
+ })
+ @Retention(RUNTIME)
+ @Target(METHOD)
+ static @interface MetaDbInitializers {
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/MultipleDataSourcesAndTransactionManagersDatabaseInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/MultipleDataSourcesAndTransactionManagersDatabaseInitializerTests.java
new file mode 100644
index 000000000000..138a81be3220
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/MultipleDataSourcesAndTransactionManagersDatabaseInitializerTests.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import javax.sql.DataSource;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+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.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.transaction.PlatformTransactionManager;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests for {@link DatabaseInitializer @DatabaseInitializer} that
+ * verify support for multiple {@link DataSource}s and {@link PlatformTransactionManager}s.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ * @see MultipleDataSourcesAndTransactionManagersTransactionalDatabaseInitializerTests
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@DirtiesContext
+public class MultipleDataSourcesAndTransactionManagersDatabaseInitializerTests {
+
+ @Autowired
+ private DataSource dataSource1;
+
+ @Autowired
+ private DataSource dataSource2;
+
+
+ @Test
+ @DatabaseInitializer(scripts = "data-add-dogbert.sql", dataSource = "dataSource1", transactionManager = "txMgr1")
+ public void database1() {
+ assertUsersExist(new JdbcTemplate(dataSource1), "Dilbert", "Dogbert");
+ }
+
+ @Test
+ @DatabaseInitializer(scripts = "data-add-catbert.sql", dataSource = "dataSource2", transactionManager = "txMgr2")
+ public void database2() {
+ assertUsersExist(new JdbcTemplate(dataSource2), "Dilbert", "Catbert");
+ }
+
+ private void assertUsersExist(JdbcTemplate jdbcTemplate, String... users) {
+ String query = "select count(name) from user where name =?";
+ for (String user : users) {
+ assertTrue("User [" + user + "] must exist.",
+ jdbcTemplate.queryForObject(query, Integer.class, user).intValue() == 1);
+ }
+ }
+
+
+ @Configuration
+ static class Config {
+
+ @Bean
+ public PlatformTransactionManager txMgr1() {
+ return new DataSourceTransactionManager(dataSource1());
+ }
+
+ @Bean
+ public PlatformTransactionManager txMgr2() {
+ return new DataSourceTransactionManager(dataSource2());
+ }
+
+ @Bean
+ public DataSource dataSource1() {
+ return new EmbeddedDatabaseBuilder()//
+ .setName("database1")//
+ .addScript("classpath:/org/springframework/test/context/jdbc/schema.sql")//
+ .addScript("classpath:/org/springframework/test/context/jdbc/data.sql")//
+ .build();
+ }
+
+ @Bean
+ public DataSource dataSource2() {
+ return new EmbeddedDatabaseBuilder()//
+ .setName("database2")//
+ .addScript("classpath:/org/springframework/test/context/jdbc/schema.sql")//
+ .addScript("classpath:/org/springframework/test/context/jdbc/data.sql")//
+ .build();
+ }
+
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/MultipleDataSourcesAndTransactionManagersTransactionalDatabaseInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/MultipleDataSourcesAndTransactionManagersTransactionalDatabaseInitializerTests.java
new file mode 100644
index 000000000000..a76af5424723
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/MultipleDataSourcesAndTransactionManagersTransactionalDatabaseInitializerTests.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import javax.sql.DataSource;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+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.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.Transactional;
+
+import static org.junit.Assert.*;
+
+/**
+ * Exact copy of {@link MultipleDataSourcesAndTransactionManagersDatabaseInitializerTests},
+ * except that the test methods are transactional.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration
+@DirtiesContext
+public class MultipleDataSourcesAndTransactionManagersTransactionalDatabaseInitializerTests {
+
+ @Autowired
+ private DataSource dataSource1;
+
+ @Autowired
+ private DataSource dataSource2;
+
+
+ @Test
+ @Transactional("txMgr1")
+ @DatabaseInitializer(scripts = "data-add-dogbert.sql", dataSource = "dataSource1", transactionManager = "txMgr1")
+ public void database1() {
+ assertUsersExist(new JdbcTemplate(dataSource1), "Dilbert", "Dogbert");
+ }
+
+ @Test
+ @Transactional("txMgr2")
+ @DatabaseInitializer(scripts = "data-add-catbert.sql", dataSource = "dataSource2", transactionManager = "txMgr2")
+ public void database2() {
+ assertUsersExist(new JdbcTemplate(dataSource2), "Dilbert", "Catbert");
+ }
+
+ private void assertUsersExist(JdbcTemplate jdbcTemplate, String... users) {
+ String query = "select count(name) from user where name =?";
+ for (String user : users) {
+ assertTrue("User [" + user + "] must exist.",
+ jdbcTemplate.queryForObject(query, Integer.class, user).intValue() == 1);
+ }
+ }
+
+
+ @Configuration
+ static class Config {
+
+ @Bean
+ public PlatformTransactionManager txMgr1() {
+ return new DataSourceTransactionManager(dataSource1());
+ }
+
+ @Bean
+ public PlatformTransactionManager txMgr2() {
+ return new DataSourceTransactionManager(dataSource2());
+ }
+
+ @Bean
+ public DataSource dataSource1() {
+ return new EmbeddedDatabaseBuilder()//
+ .setName("database1")//
+ .addScript("classpath:/org/springframework/test/context/jdbc/schema.sql")//
+ .addScript("classpath:/org/springframework/test/context/jdbc/data.sql")//
+ .build();
+ }
+
+ @Bean
+ public DataSource dataSource2() {
+ return new EmbeddedDatabaseBuilder()//
+ .setName("database2")//
+ .addScript("classpath:/org/springframework/test/context/jdbc/schema.sql")//
+ .addScript("classpath:/org/springframework/test/context/jdbc/data.sql")//
+ .build();
+ }
+
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/NonTransactionalDatabaseInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/NonTransactionalDatabaseInitializerTests.java
new file mode 100644
index 000000000000..4ebd8777462e
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/NonTransactionalDatabaseInitializerTests.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import javax.sql.DataSource;
+
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.jdbc.JdbcTestUtils;
+
+import static org.junit.Assert.*;
+
+/**
+ * Integration tests which verify that scripts executed via
+ * {@link DatabaseInitializer @DatabaseInitializer} will persist between
+ * non-transactional test methods.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@ContextConfiguration(classes = EmptyDatabaseConfig.class)
+@DatabaseInitializer({ "schema.sql", "data.sql" })
+@DirtiesContext
+public class NonTransactionalDatabaseInitializerTests {
+
+ protected JdbcTemplate jdbcTemplate;
+
+
+ @Autowired
+ public void setDataSource(DataSource dataSource) {
+ this.jdbcTemplate = new JdbcTemplate(dataSource);
+ }
+
+ @Test
+ // test##_ prefix is required for @FixMethodOrder.
+ public void test01_classLevelScripts() {
+ assertNumUsers(1);
+ }
+
+ @Test
+ @DatabaseInitializers(@DatabaseInitializer("data-add-dogbert.sql"))
+ // test##_ prefix is required for @FixMethodOrder.
+ public void test02_methodLevelScripts() {
+ assertNumUsers(2);
+ }
+
+ protected void assertNumUsers(int expected) {
+ assertEquals("Number of rows in the 'user' table.", expected,
+ JdbcTestUtils.countRowsInTable(jdbcTemplate, "user"));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/PopulatedSchemaDatabaseConfig.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/PopulatedSchemaDatabaseConfig.java
new file mode 100644
index 000000000000..1c9573dae1e2
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/PopulatedSchemaDatabaseConfig.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import javax.sql.DataSource;
+
+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.transaction.PlatformTransactionManager;
+
+/**
+ * Database configuration class for SQL script integration tests with the 'user'
+ * table already created.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@Configuration
+public class PopulatedSchemaDatabaseConfig {
+
+ @Bean
+ public PlatformTransactionManager transactionManager() {
+ return new DataSourceTransactionManager(dataSource());
+ }
+
+ @Bean
+ public DataSource dataSource() {
+ return new EmbeddedDatabaseBuilder()//
+ .addScript("classpath:/org/springframework/test/context/jdbc/schema.sql") //
+ .build();
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/PopulatedSchemaTransactionalDatabaseInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/PopulatedSchemaTransactionalDatabaseInitializerTests.java
new file mode 100644
index 000000000000..b615f6cf4f30
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/PopulatedSchemaTransactionalDatabaseInitializerTests.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import org.junit.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
+import org.springframework.test.context.transaction.AfterTransaction;
+import org.springframework.test.context.transaction.BeforeTransaction;
+
+import static org.junit.Assert.*;
+
+/**
+ * Transactional integration tests that verify rollback semantics for
+ * {@link DatabaseInitializer @DatabaseInitializer} support.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@ContextConfiguration(classes = PopulatedSchemaDatabaseConfig.class)
+@DirtiesContext
+public class PopulatedSchemaTransactionalDatabaseInitializerTests extends AbstractTransactionalJUnit4SpringContextTests {
+
+ @BeforeTransaction
+ @AfterTransaction
+ public void verifyPreAndPostTransactionDatabaseState() {
+ assertNumUsers(0);
+ }
+
+ @Test
+ @DatabaseInitializers(@DatabaseInitializer("data-add-dogbert.sql"))
+ public void methodLevelScripts() {
+ assertNumUsers(1);
+ }
+
+ protected void assertNumUsers(int expected) {
+ assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/RequiresNewTransactionDatabaseInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/RequiresNewTransactionDatabaseInitializerTests.java
new file mode 100644
index 000000000000..31e5d3535359
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/RequiresNewTransactionDatabaseInitializerTests.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import org.junit.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
+import org.springframework.test.context.transaction.AfterTransaction;
+import org.springframework.test.context.transaction.BeforeTransaction;
+
+import static org.junit.Assert.*;
+
+/**
+ * Transactional integration tests that verify commit semantics for
+ * {@link DatabaseInitializer#requireNewTransaction}.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@ContextConfiguration(classes = PopulatedSchemaDatabaseConfig.class)
+@DirtiesContext
+public class RequiresNewTransactionDatabaseInitializerTests extends AbstractTransactionalJUnit4SpringContextTests {
+
+ @BeforeTransaction
+ public void beforeTransaction() {
+ assertNumUsers(0);
+ }
+
+ @Test
+ @DatabaseInitializers(@DatabaseInitializer(scripts = "data-add-dogbert.sql", requireNewTransaction = true))
+ public void methodLevelScripts() {
+ assertNumUsers(1);
+ }
+
+ @AfterTransaction
+ public void afterTransaction() {
+ assertNumUsers(1);
+ }
+
+ protected void assertNumUsers(int expected) {
+ assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/TransactionalAfterTestMethodDatabaseInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/TransactionalAfterTestMethodDatabaseInitializerTests.java
new file mode 100644
index 000000000000..632864da18ac
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/TransactionalAfterTestMethodDatabaseInitializerTests.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import org.junit.FixMethodOrder;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runners.MethodSorters;
+import org.springframework.jdbc.BadSqlGrammarException;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.jdbc.DatabaseInitializer.ExecutionPhase;
+import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
+import org.springframework.test.context.transaction.AfterTransaction;
+
+import static org.junit.Assert.*;
+
+/**
+ * Transactional integration tests for {@link DatabaseInitializer @DatabaseInitializer}
+ * that verify proper support for {@link ExecutionPhase#AFTER_TEST_METHOD}.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@ContextConfiguration(classes = EmptyDatabaseConfig.class)
+@DirtiesContext
+public class TransactionalAfterTestMethodDatabaseInitializerTests extends AbstractTransactionalJUnit4SpringContextTests {
+
+ @Rule
+ public TestName testName = new TestName();
+
+
+ @AfterTransaction
+ public void afterTransaction() {
+ if ("test01".equals(testName.getMethodName())) {
+ try {
+ assertNumUsers(99);
+ fail("Should throw a BadSqlGrammarException after test01, assuming 'drop-schema.sql' was executed");
+ }
+ catch (BadSqlGrammarException e) {
+ /* expected */
+ }
+ }
+ }
+
+ @Test
+ @DatabaseInitializers({//
+ @DatabaseInitializer({ "schema.sql", "data.sql" }),//
+ @DatabaseInitializer(scripts = "drop-schema.sql", executionPhase = ExecutionPhase.AFTER_TEST_METHOD) //
+ })
+ // test## is required for @FixMethodOrder.
+ public void test01() {
+ assertNumUsers(1);
+ }
+
+ @Test
+ @DatabaseInitializers(@DatabaseInitializer({ "schema.sql", "data.sql", "data-add-dogbert.sql" }))
+ // test## is required for @FixMethodOrder.
+ public void test02() {
+ assertNumUsers(2);
+ }
+
+ protected void assertNumUsers(int expected) {
+ assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/TransactionalDatabaseInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/TransactionalDatabaseInitializerTests.java
new file mode 100644
index 000000000000..96c313fbfd9d
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/TransactionalDatabaseInitializerTests.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2002-2014 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.jdbc;
+
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
+
+import static org.junit.Assert.*;
+
+/**
+ * Transactional integration tests for {@link DatabaseInitializer @DatabaseInitializer}
+ * support.
+ *
+ * @author Sam Brannen
+ * @since 4.1
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@ContextConfiguration(classes = EmptyDatabaseConfig.class)
+@DatabaseInitializers(@DatabaseInitializer({ "schema.sql", "data.sql" }))
+@DirtiesContext
+public class TransactionalDatabaseInitializerTests extends AbstractTransactionalJUnit4SpringContextTests {
+
+ @Test
+ // test##_ prefix is required for @FixMethodOrder.
+ public void test01_classLevelScripts() {
+ assertNumUsers(1);
+ }
+
+ @Test
+ @DatabaseInitializers(@DatabaseInitializer({ "drop-schema.sql", "schema.sql", "data.sql", "data-add-dogbert.sql" }))
+ // test##_ prefix is required for @FixMethodOrder.
+ public void test02_methodLevelScripts() {
+ assertNumUsers(2);
+ }
+
+ protected void assertNumUsers(int expected) {
+ assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/data-add-catbert.sql b/spring-test/src/test/java/org/springframework/test/context/jdbc/data-add-catbert.sql
new file mode 100644
index 000000000000..6c034c10fa35
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/data-add-catbert.sql
@@ -0,0 +1 @@
+INSERT INTO user VALUES('Catbert');
\ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/data-add-dogbert.sql b/spring-test/src/test/java/org/springframework/test/context/jdbc/data-add-dogbert.sql
new file mode 100644
index 000000000000..f20b0a368fcb
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/data-add-dogbert.sql
@@ -0,0 +1 @@
+INSERT INTO user VALUES('Dogbert');
\ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/data-add-users-with-custom-script-syntax.sql b/spring-test/src/test/java/org/springframework/test/context/jdbc/data-add-users-with-custom-script-syntax.sql
new file mode 100644
index 000000000000..664a6998e5e0
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/data-add-users-with-custom-script-syntax.sql
@@ -0,0 +1,22 @@
+` custom single-line comment
+
+#$ custom
+block
+comment
+$#
+
+INSERT
+
+INTO
+
+user
+
+VALUES('Dilbert')
+
+@@
+
+` custom single-line comment
+
+
+INSERT INTO user VALUES('Dogbert')@@
+INSERT INTO user VALUES('Catbert')@@
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/data.sql b/spring-test/src/test/java/org/springframework/test/context/jdbc/data.sql
new file mode 100644
index 000000000000..182a36582b8c
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/data.sql
@@ -0,0 +1 @@
+INSERT INTO user VALUES('Dilbert');
\ No newline at end of file
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/drop-schema.sql b/spring-test/src/test/java/org/springframework/test/context/jdbc/drop-schema.sql
new file mode 100644
index 000000000000..3a28aade0c42
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/drop-schema.sql
@@ -0,0 +1 @@
+DROP TABLE user IF EXISTS;
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/schema.sql b/spring-test/src/test/java/org/springframework/test/context/jdbc/schema.sql
new file mode 100644
index 000000000000..8b0abd27206d
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/schema.sql
@@ -0,0 +1,4 @@
+CREATE TABLE user (
+ name VARCHAR(20) NOT NULL,
+ PRIMARY KEY(name)
+);
diff --git a/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsMergedConfigTests.java b/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsMergedConfigTests.java
index 2d84d022f2c0..f7eb5600af08 100644
--- a/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsMergedConfigTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/support/ContextLoaderUtilsMergedConfigTests.java
@@ -44,7 +44,7 @@ public void buildMergedConfigWithBareAnnotations() {
assertMergedConfig(
mergedConfig,
testClass,
- new String[] { "classpath:/org/springframework/test/context/support/AbstractContextLoaderUtilsTests$BareAnnotations-context.xml" },
+ new String[] { "classpath:org/springframework/test/context/support/AbstractContextLoaderUtilsTests$BareAnnotations-context.xml" },
EMPTY_CLASS_ARRAY, DelegatingSmartContextLoader.class);
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests.java b/spring-test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests.java
index bf53acc54259..0678b7a7659d 100644
--- a/spring-test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2014 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.
@@ -96,7 +96,7 @@ class ExplicitMixedPathTypesLocationsTestCase {
{
ClasspathExistentDefaultLocationsTestCase.class,
- new String[] { "classpath:/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests$1ClasspathExistentDefaultLocationsTestCase-context.xml" } },
+ new String[] { "classpath:org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests$1ClasspathExistentDefaultLocationsTestCase-context.xml" } },
{
ImplicitClasspathLocationsTestCase.class,
diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java
index fd6fd9c66c6d..71b30a465b63 100644
--- a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java
@@ -192,13 +192,13 @@ public void afterTestMethodWithAfterTransactionDeclaredViaMetaAnnotation() throw
@Test
public void retrieveConfigurationAttributesWithMissingTransactionConfiguration() throws Exception {
- assertTransactionConfigurationAttributes(MissingTransactionConfigurationTestCase.class, "transactionManager",
+ assertTransactionConfigurationAttributes(MissingTransactionConfigurationTestCase.class, "",
true);
}
@Test
public void retrieveConfigurationAttributesWithEmptyTransactionConfiguration() throws Exception {
- assertTransactionConfigurationAttributes(EmptyTransactionConfigurationTestCase.class, "transactionManager",
+ assertTransactionConfigurationAttributes(EmptyTransactionConfigurationTestCase.class, "",
true);
}