diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java
index d1348ac13a2..fb69e925c0b 100644
--- a/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java
+++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java
@@ -31,8 +31,8 @@ import org.springframework.core.annotation.AliasFor;
* SQL {@link #scripts} and {@link #statements} to be executed against a given
* database during integration tests.
*
- *
Method-level declarations override class-level declarations by default.
- * This behavior can be adjusted by setting the {@link #mergeMode}.
+ *
Method-level declarations override class-level declarations by default,
+ * but this behavior can be configured via {@link SqlMergeMode @SqlMergeMode}.
*
*
Script execution is performed by the {@link SqlScriptsTestExecutionListener},
* which is enabled by default.
@@ -54,9 +54,9 @@ import org.springframework.core.annotation.AliasFor;
* composed annotations with attribute overrides.
*
* @author Sam Brannen
- * @author Dmitry Semukhin
* @since 4.1
* @see SqlConfig
+ * @see SqlMergeMode
* @see SqlGroup
* @see SqlScriptsTestExecutionListener
* @see org.springframework.transaction.annotation.Transactional
@@ -139,16 +139,6 @@ public @interface Sql {
*/
ExecutionPhase executionPhase() default ExecutionPhase.BEFORE_TEST_METHOD;
- /**
- * Indicates whether this {@code @Sql} annotation should be merged with
- * class-level {@code @Sql} annotations or override them.
- *
The merge mode is ignored if declared in a class-level {@code @Sql}
- * annotation.
- *
Defaults to {@link MergeMode#OVERRIDE OVERRIDE} for backwards compatibility.
- * @since 5.2
- */
- MergeMode mergeMode() default MergeMode.OVERRIDE;
-
/**
* Local configuration for the SQL scripts and statements declared within
* this {@code @Sql} annotation.
@@ -177,24 +167,4 @@ public @interface Sql {
AFTER_TEST_METHOD
}
- /**
- * Enumeration of modes that dictate whether method-level {@code @Sql}
- * declarations are merged with class-level {@code @Sql} declarations.
- * @since 5.2
- */
- enum MergeMode {
-
- /**
- * Indicates that method-level {@code @Sql} declarations should override
- * class-level {@code @Sql} declarations.
- */
- OVERRIDE,
-
- /**
- * Indicates that method-level {@code @Sql} declarations should be merged
- * with class-level {@code @Sql} declarations.
- */
- MERGE
- }
-
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlMergeMode.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlMergeMode.java
new file mode 100644
index 00000000000..352eb68da97
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlMergeMode.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2002-2019 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
+ *
+ * https://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.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@code @SqlMergeMode} is used to annotate a test class or test method to
+ * configure whether method-level {@code @Sql} declarations are merged with
+ * class-level {@code @Sql} declarations.
+ *
+ *
A method-level {@code @SqlMergeMode} declaration overrides a class-level
+ * declaration.
+ *
+ *
If {@code @SqlMergeMode} is not declared on a test class or test method,
+ * {@link MergeMode#OVERRIDE} will be used by default.
+ *
+ *
This annotation may be used as a meta-annotation to create custom
+ * composed annotations with attribute overrides.
+ *
+ * @author Sam Brannen
+ * @author Dmitry Semukhin
+ * @since 5.2
+ * @see Sql
+ * @see MergeMode#MERGE
+ * @see MergeMode#OVERRIDE
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface SqlMergeMode {
+
+ /**
+ * Indicates whether method-level {@code @Sql} annotations should be merged
+ * with class-level {@code @Sql} annotations or override them.
+ */
+ MergeMode value();
+
+
+ /**
+ * Enumeration of modes that dictate whether method-level {@code @Sql}
+ * declarations are merged with class-level {@code @Sql} declarations.
+ */
+ enum MergeMode {
+
+ /**
+ * Indicates that method-level {@code @Sql} declarations should be merged
+ * with class-level {@code @Sql} declarations, with class-level SQL
+ * scripts and statements executed before method-level scripts and
+ * statements.
+ */
+ MERGE,
+
+ /**
+ * Indicates that method-level {@code @Sql} declarations should override
+ * class-level {@code @Sql} declarations.
+ */
+ OVERRIDE
+
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java
index 06751e0f645..f824a363c85 100644
--- a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java
+++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlScriptsTestExecutionListener.java
@@ -20,7 +20,6 @@ import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
-import java.util.stream.Collectors;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
@@ -38,6 +37,7 @@ import org.springframework.test.context.TestContext;
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
import org.springframework.test.context.jdbc.SqlConfig.ErrorMode;
import org.springframework.test.context.jdbc.SqlConfig.TransactionMode;
+import org.springframework.test.context.jdbc.SqlMergeMode.MergeMode;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.test.context.transaction.TestContextTransactionUtils;
import org.springframework.test.context.util.TestContextResourceUtils;
@@ -130,36 +130,57 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
* {@link TestContext} and {@link ExecutionPhase}.
*/
private void executeSqlScripts(TestContext testContext, ExecutionPhase executionPhase) {
- Set methodLevelSqls = getSqlAnnotationsFor(testContext.getTestMethod());
- List methodLevelOverrides = methodLevelSqls.stream()
- .filter(s -> s.executionPhase() == executionPhase)
- .filter(s -> s.mergeMode() == Sql.MergeMode.OVERRIDE)
- .collect(Collectors.toList());
- if (methodLevelOverrides.isEmpty()) {
- executeScripts(getSqlAnnotationsFor(testContext.getTestClass()), testContext, executionPhase, true);
- executeScripts(methodLevelSqls, testContext, executionPhase, false);
- } else {
- executeScripts(methodLevelOverrides, testContext, executionPhase, false);
+ Method testMethod = testContext.getTestMethod();
+ Class> testClass = testContext.getTestClass();
+
+ if (mergeSqlAnnotations(testContext)) {
+ executeSqlScripts(getSqlAnnotationsFor(testClass), testContext, executionPhase, true);
+ executeSqlScripts(getSqlAnnotationsFor(testMethod), testContext, executionPhase, false);
+ }
+ else {
+ Set methodLevelSqlAnnotations = getSqlAnnotationsFor(testMethod);
+ if (!methodLevelSqlAnnotations.isEmpty()) {
+ executeSqlScripts(methodLevelSqlAnnotations, testContext, executionPhase, false);
+ }
+ else {
+ executeSqlScripts(getSqlAnnotationsFor(testClass), testContext, executionPhase, true);
+ }
+ }
+ }
+
+ /**
+ * Determine if method-level {@code @Sql} annotations should be merged with
+ * class-level {@code @Sql} annotations.
+ */
+ private boolean mergeSqlAnnotations(TestContext testContext) {
+ SqlMergeMode sqlMergeMode = getSqlMergeModeFor(testContext.getTestMethod());
+ if (sqlMergeMode == null) {
+ sqlMergeMode = getSqlMergeModeFor(testContext.getTestClass());
}
+ return (sqlMergeMode != null && sqlMergeMode.value() == MergeMode.MERGE);
+ }
+
+ /**
+ * Get the {@code @SqlMergeMode} annotation declared on the supplied {@code element}.
+ */
+ private SqlMergeMode getSqlMergeModeFor(AnnotatedElement element) {
+ return AnnotatedElementUtils.findMergedAnnotation(element, SqlMergeMode.class);
}
/**
- * Get the {@link Sql @Sql} annotations declared on the supplied
- * {@link AnnotatedElement}.
+ * Get the {@code @Sql} annotations declared on the supplied {@code element}.
*/
- private Set getSqlAnnotationsFor(AnnotatedElement annotatedElement) {
- return AnnotatedElementUtils.getMergedRepeatableAnnotations(annotatedElement, Sql.class, SqlGroup.class);
+ private Set getSqlAnnotationsFor(AnnotatedElement element) {
+ return AnnotatedElementUtils.getMergedRepeatableAnnotations(element, Sql.class, SqlGroup.class);
}
/**
* Execute SQL scripts for the supplied {@link Sql @Sql} annotations.
*/
- private void executeScripts(
- Iterable scripts, TestContext testContext, ExecutionPhase executionPhase, boolean classLevel) {
+ private void executeSqlScripts(
+ Set sqlAnnotations, TestContext testContext, ExecutionPhase executionPhase, boolean classLevel) {
- for (Sql sql : scripts) {
- executeSqlScripts(sql, executionPhase, testContext, classLevel);
- }
+ sqlAnnotations.forEach(sql -> executeSqlScripts(sql, executionPhase, testContext, classLevel));
}
/**
@@ -196,7 +217,7 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
}
}
- ResourceDatabasePopulator populator = configurePopulator(mergedSqlConfig);
+ ResourceDatabasePopulator populator = createDatabasePopulator(mergedSqlConfig);
populator.setScripts(scriptResources.toArray(new Resource[0]));
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL scripts: " + ObjectUtils.nullSafeToString(scriptResources));
@@ -242,7 +263,7 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
}
@NonNull
- private ResourceDatabasePopulator configurePopulator(MergedSqlConfig mergedSqlConfig) {
+ private ResourceDatabasePopulator createDatabasePopulator(MergedSqlConfig mergedSqlConfig) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.setSqlScriptEncoding(mergedSqlConfig.getEncoding());
populator.setSeparator(mergedSqlConfig.getSeparator());
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/SqlMethodOverrideTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/SqlMethodOverrideTests.java
deleted file mode 100644
index a33731dcd5d..00000000000
--- a/spring-test/src/test/java/org/springframework/test/context/jdbc/SqlMethodOverrideTests.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2002-2019 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
- *
- * https://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.assertEquals;
-import static org.springframework.test.context.jdbc.Sql.MergeMode.OVERRIDE;
-
-/**
- * Transactional integration tests for {@link Sql @Sql} that verify proper
- * overriding support for class-level and method-level declarations.
- *
- * @author Dmitry Semukhin
- * @author Sam Brannen
- * @since 5.2
- */
-@ContextConfiguration(classes = EmptyDatabaseConfig.class)
-@Sql({ "schema.sql", "data-add-catbert.sql" })
-@DirtiesContext
-public class SqlMethodOverrideTests extends AbstractTransactionalJUnit4SpringContextTests {
-
- @Test
- @Sql(
- scripts = { "schema.sql", "data.sql", "data-add-dogbert.sql", "data-add-catbert.sql" },
- mergeMode = OVERRIDE
- )
- public void methodLevelSqlScriptsOverrideClassLevelScripts() {
- 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/SqlMethodMergeTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/AbstractSqlMergeModeTests.java
similarity index 54%
rename from spring-test/src/test/java/org/springframework/test/context/jdbc/SqlMethodMergeTests.java
rename to spring-test/src/test/java/org/springframework/test/context/jdbc/merging/AbstractSqlMergeModeTests.java
index 453c44af762..bc54dc2ecbc 100644
--- a/spring-test/src/test/java/org/springframework/test/context/jdbc/SqlMethodMergeTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/AbstractSqlMergeModeTests.java
@@ -14,38 +14,32 @@
* limitations under the License.
*/
-package org.springframework.test.context.jdbc;
+package org.springframework.test.context.jdbc.merging;
-import org.junit.Test;
+import java.util.List;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.jdbc.EmptyDatabaseConfig;
+import org.springframework.test.context.jdbc.SqlMergeMode;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
-import static org.junit.Assert.assertEquals;
-import static org.springframework.test.context.jdbc.Sql.MergeMode.MERGE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.test.annotation.DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD;
/**
- * Transactional integration tests for {@link Sql @Sql} that verify proper
- * merging support for class-level and method-level declarations.
+ * Abstract base class for tests involving {@link SqlMergeMode @SqlMergeMode}.
*
- * @author Dmitry Semukhin
* @author Sam Brannen
* @since 5.2
*/
@ContextConfiguration(classes = EmptyDatabaseConfig.class)
-@Sql({ "schema.sql", "data-add-catbert.sql" })
-@DirtiesContext
-public class SqlMethodMergeTests extends AbstractTransactionalJUnit4SpringContextTests {
+@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
+abstract class AbstractSqlMergeModeTests extends AbstractTransactionalJUnit4SpringContextTests {
- @Test
- @Sql(scripts = "data-add-dogbert.sql", mergeMode = MERGE)
- public void testMerge() {
- assertNumUsers(2);
- }
-
- protected void assertNumUsers(int expected) {
- assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
+ protected void assertUsers(String... expectedUsers) {
+ List actualUsers = super.jdbcTemplate.queryForList("select name from user", String.class);
+ assertThat(actualUsers).containsExactlyInAnyOrder(expectedUsers);
}
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/ClassLevelMergeSqlMergeModeTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/ClassLevelMergeSqlMergeModeTests.java
new file mode 100644
index 00000000000..6b79558e144
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/ClassLevelMergeSqlMergeModeTests.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2002-2019 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
+ *
+ * https://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.merging;
+
+import org.junit.Test;
+
+import org.springframework.test.context.jdbc.Sql;
+import org.springframework.test.context.jdbc.SqlMergeMode;
+
+import static org.springframework.test.context.jdbc.SqlMergeMode.MergeMode.MERGE;
+import static org.springframework.test.context.jdbc.SqlMergeMode.MergeMode.OVERRIDE;
+
+/**
+ * Transactional integration tests that verify proper merging and overriding support
+ * for class-level and method-level {@link Sql @Sql} declarations when
+ * {@link SqlMergeMode @SqlMergeMode} is declared at the class level with
+ * {@link SqlMergeMode.MergeMode#MERGE MERGE} mode.
+ *
+ * @author Sam Brannen
+ * @author Dmitry Semukhin
+ * @since 5.2
+ */
+@Sql({ "../schema.sql", "../data-add-catbert.sql" })
+@SqlMergeMode(MERGE)
+public class ClassLevelMergeSqlMergeModeTests extends AbstractSqlMergeModeTests {
+
+ @Test
+ public void classLevelScripts() {
+ assertUsers("Catbert");
+ }
+
+ @Test
+ @Sql("../data-add-dogbert.sql")
+ public void merged() {
+ assertUsers("Catbert", "Dogbert");
+ }
+
+ @Test
+ @Sql({ "../schema.sql", "../data.sql", "../data-add-dogbert.sql", "../data-add-catbert.sql" })
+ @SqlMergeMode(OVERRIDE)
+ public void overridden() {
+ assertUsers("Dilbert", "Dogbert", "Catbert");
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/ClassLevelOverrideSqlMergeModeTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/ClassLevelOverrideSqlMergeModeTests.java
new file mode 100644
index 00000000000..f5464bdbedc
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/ClassLevelOverrideSqlMergeModeTests.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2002-2019 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
+ *
+ * https://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.merging;
+
+import org.junit.Test;
+
+import org.springframework.test.context.jdbc.Sql;
+import org.springframework.test.context.jdbc.SqlMergeMode;
+
+import static org.springframework.test.context.jdbc.SqlMergeMode.MergeMode.MERGE;
+import static org.springframework.test.context.jdbc.SqlMergeMode.MergeMode.OVERRIDE;
+
+/**
+ * Transactional integration tests that verify proper merging and overriding support
+ * for class-level and method-level {@link Sql @Sql} declarations when
+ * {@link SqlMergeMode @SqlMergeMode} is declared at the class level with
+ * {@link SqlMergeMode.MergeMode#OVERRIDE OVERRIDE} mode.
+ *
+ * @author Sam Brannen
+ * @author Dmitry Semukhin
+ * @since 5.2
+ */
+@Sql({ "../schema.sql", "../data-add-catbert.sql" })
+@SqlMergeMode(OVERRIDE)
+public class ClassLevelOverrideSqlMergeModeTests extends AbstractSqlMergeModeTests {
+
+ @Test
+ public void classLevelScripts() {
+ assertUsers("Catbert");
+ }
+
+ @Test
+ @Sql("../data-add-dogbert.sql")
+ @SqlMergeMode(MERGE)
+ public void merged() {
+ assertUsers("Catbert", "Dogbert");
+ }
+
+ @Test
+ @Sql({ "../schema.sql", "../data.sql", "../data-add-dogbert.sql", "../data-add-catbert.sql" })
+ public void overridden() {
+ assertUsers("Dilbert", "Dogbert", "Catbert");
+ }
+
+}