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. + *

+ */ + 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: + *

    + *
  1. There is only one bean of type {@code DataSource} in the test's + * {@code ApplicationContext}.
  2. + *
  3. The {@code DataSource} to use is named {@code "dataSource"}.
  4. + *
+ */ + 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: + *

    + *
  1. There is only one bean of type {@code PlatformTransactionManager} in + * the test's {@code ApplicationContext}.
  2. + *
  3. {@link org.springframework.transaction.annotation.TransactionManagementConfigurer + * TransactionManagementConfigurer} has been implemented to specify which + * {@code PlatformTransactionManager} bean should be used for annotation-driven + * transaction management.
  4. + *
  5. The {@code PlatformTransactionManager} to use is named + * {@code "transactionManager"}.
  6. + *
+ */ + 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> 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: + *

      + *
    1. Look up the {@code DataSource} by type and name, if the supplied + * {@code name} is non-empty. + *
    2. Look up the {@code DataSource} by type. + *
    3. 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: + *

        + *
      1. Look up the transaction manager by type and name, if the supplied + * {@code name} is non-empty. + *
      2. Look up the transaction manager by type. + *
      3. Look up the transaction manager via a {@link TransactionManagementConfigurer}, + * if present. + *
      4. 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: + *

            + *
          1. There is only one bean of type {@code PlatformTransactionManager} in + * the test's {@code ApplicationContext}.
          2. *
          3. {@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.
      5. + *
      6. The {@code PlatformTransactionManager} to use is named + * {@code "transactionManager"}.
      7. + *
      * *

      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); }