getSqlAnnotationsFor(Method method) {
+ return AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Sql.class, SqlGroup.class);
}
/**
diff --git a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java
index 20abe3e772..01beb1b6cb 100644
--- a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java
@@ -17,6 +17,7 @@
package org.springframework.test.util;
import java.lang.annotation.Annotation;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
@@ -85,12 +86,16 @@ public abstract class MetaAnnotationUtils {
* {@linkplain Class#getEnclosingClass() enclosing class} hierarchy of the
* supplied class. The enclosing class hierarchy will only be searched if
* appropriate.
+ * {@link org.springframework.core.annotation.AliasFor @AliasFor} semantics
+ * are fully supported, both within a single annotation and within annotation
+ * hierarchies.
* @param clazz the class to look for annotations on
* @param annotationType the type of annotation to look for
* @return the merged, synthesized {@code Annotation}, or {@code null} if not found
* @since 5.3
* @see AnnotatedElementUtils#findMergedAnnotation(java.lang.reflect.AnnotatedElement, Class)
* @see #findAnnotationDescriptor(Class, Class)
+ * @see #searchEnclosingClass(Class)
*/
@Nullable
public static T findMergedAnnotation(Class> clazz, Class annotationType) {
@@ -106,6 +111,53 @@ public abstract class MetaAnnotationUtils {
return (descriptor != null ? descriptor.synthesizeAnnotation() : null);
}
+ /**
+ * Get all repeatable annotations of the specified {@code annotationType}
+ * within the annotation hierarchy above the supplied class; and for
+ * each annotation found, merge that annotation's attributes with matching
+ * attributes from annotations in lower levels of the annotation hierarchy and
+ * synthesize the results back into an annotation of the specified {@code annotationType}.
+ * This method will find {@link java.lang.annotation.Inherited @Inherited}
+ * annotations declared on superclasses if the supplied class does not have
+ * any local declarations of the repeatable annotation. If no inherited
+ * annotations are found, this method will search within the
+ * {@linkplain Class#getEnclosingClass() enclosing class} hierarchy of the
+ * supplied class. The enclosing class hierarchy will only be searched if
+ * appropriate.
+ *
The container type that holds the repeatable annotations will be looked up
+ * via {@link java.lang.annotation.Repeatable}.
+ *
{@link org.springframework.core.annotation.AliasFor @AliasFor} semantics
+ * are fully supported, both within a single annotation and within annotation
+ * hierarchies.
+ * @param clazz the class on which to search for annotations (never {@code null})
+ * @param annotationType the annotation type to find (never {@code null})
+ * @return the set of all merged repeatable annotations found, or an empty set
+ * if none were found
+ * @since 5.3
+ * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(java.lang.reflect.AnnotatedElement, Class)
+ * @see #searchEnclosingClass(Class)
+ */
+ public static Set getMergedRepeatableAnnotations(
+ Class> clazz, Class annotationType) {
+
+ // Present (via @Inherited semantics), directly present, or meta-present?
+ Set mergedAnnotations = MergedAnnotations.from(clazz, SearchStrategy.INHERITED_ANNOTATIONS)
+ .stream(annotationType)
+ .collect(MergedAnnotationCollectors.toAnnotationSet());
+
+ if (!mergedAnnotations.isEmpty()) {
+ return mergedAnnotations;
+ }
+
+ // Declared on an enclosing class of an inner class?
+ if (searchEnclosingClass(clazz)) {
+ // Then mimic @Inherited semantics within the enclosing class hierarchy.
+ return getMergedRepeatableAnnotations(clazz.getEnclosingClass(), annotationType);
+ }
+
+ return Collections.emptySet();
+ }
+
/**
* Find the {@link AnnotationDescriptor} for the supplied {@code annotationType}
* on the supplied {@link Class}, traversing its annotations, interfaces, and
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/MergedSqlConfigTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/MergedSqlConfigTests.java
index d9cc836356..e38d302870 100644
--- a/spring-test/src/test/java/org/springframework/test/context/jdbc/MergedSqlConfigTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/MergedSqlConfigTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2020 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.
@@ -18,6 +18,7 @@ package org.springframework.test.context.jdbc;
import java.lang.reflect.Method;
+import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -182,86 +183,62 @@ class MergedSqlConfigTests {
.withMessage("You may declare the 'commentPrefix' or 'commentPrefixes' attribute in @SqlConfig but not both");
}
- @Test
- void globalConfigWithDefaults() throws Exception {
- Method method = GlobalConfigWithDefaultsClass.class.getMethod("globalConfigMethod");
- SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
- MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, GlobalConfigWithDefaultsClass.class);
- assertDefaults(cfg);
+ @Nested
+ class TopLevelMergedSqlConfigTests {
+
+ @Test
+ void globalConfigWithDefaults() throws Exception {
+ assertGlobalConfigWithDefaults(GlobalConfigWithDefaultsClass.class);
+ }
+
+ @Test
+ void globalConfig() throws Exception {
+ assertGlobalConfig(GlobalConfigClass.class);
+ }
+
+ @Test
+ void globalConfigWithLocalOverrides() throws Exception {
+ assertGlobalConfigWithLocalOverrides(GlobalConfigClass.class);
+ }
+
+ @Test
+ void globalConfigWithCommentPrefixAndLocalOverrides() throws Exception {
+ assertGlobalConfigWithCommentPrefixAndLocalOverrides(GlobalConfigWithPrefixClass.class);
+ }
+
+ @Test
+ void globalConfigWithCommentPrefixesAndLocalOverrides() throws Exception {
+ assertGlobalConfigWithCommentPrefixesAndLocalOverrides(GlobalConfigWithPrefixesClass.class);
+ }
}
- @Test
- void globalConfig() throws Exception {
- Method method = GlobalConfigClass.class.getMethod("globalConfigMethod");
- SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
- MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, GlobalConfigClass.class);
+ @Nested
+ class NestedMergedSqlConfigTests {
- assertSoftly(softly -> {
- softly.assertThat(cfg).isNotNull();
- softly.assertThat(cfg.getDataSource()).as("dataSource").isEqualTo("");
- softly.assertThat(cfg.getTransactionManager()).as("transactionManager").isEqualTo("");
- softly.assertThat(cfg.getTransactionMode()).as("transactionMode").isEqualTo(INFERRED);
- softly.assertThat(cfg.getEncoding()).as("encoding").isEqualTo("global");
- softly.assertThat(cfg.getSeparator()).as("separator").isEqualTo("\n");
- softly.assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("`", "--"));
- softly.assertThat(cfg.getBlockCommentStartDelimiter()).as("blockCommentStartDelimiter").isEqualTo(DEFAULT_BLOCK_COMMENT_START_DELIMITER);
- softly.assertThat(cfg.getBlockCommentEndDelimiter()).as("blockCommentEndDelimiter").isEqualTo(DEFAULT_BLOCK_COMMENT_END_DELIMITER);
- softly.assertThat(cfg.getErrorMode()).as("errorMode").isEqualTo(IGNORE_FAILED_DROPS);
- });
- }
+ @Test
+ void globalConfigWithDefaults() throws Exception {
+ assertGlobalConfigWithDefaults(GlobalConfigWithDefaultsClass.Nested.class);
+ }
- @Test
- void globalConfigWithLocalOverrides() throws Exception {
- Method method = GlobalConfigClass.class.getMethod("globalConfigWithLocalOverridesMethod");
- SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
- MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, GlobalConfigClass.class);
+ @Test
+ void globalConfig() throws Exception {
+ assertGlobalConfig(GlobalConfigClass.Nested.class);
+ }
- assertSoftly(softly -> {
- softly.assertThat(cfg).isNotNull();
- softly.assertThat(cfg.getDataSource()).as("dataSource").isEqualTo("");
- softly.assertThat(cfg.getTransactionManager()).as("transactionManager").isEqualTo("");
- softly.assertThat(cfg.getTransactionMode()).as("transactionMode").isEqualTo(INFERRED);
- softly.assertThat(cfg.getEncoding()).as("encoding").isEqualTo("local");
- softly.assertThat(cfg.getSeparator()).as("separator").isEqualTo("@@");
- softly.assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("#"));
- softly.assertThat(cfg.getBlockCommentStartDelimiter()).as("blockCommentStartDelimiter").isEqualTo(DEFAULT_BLOCK_COMMENT_START_DELIMITER);
- softly.assertThat(cfg.getBlockCommentEndDelimiter()).as("blockCommentEndDelimiter").isEqualTo(DEFAULT_BLOCK_COMMENT_END_DELIMITER);
- softly.assertThat(cfg.getErrorMode()).as("errorMode").isEqualTo(CONTINUE_ON_ERROR);
- });
- }
+ @Test
+ void globalConfigWithLocalOverrides() throws Exception {
+ assertGlobalConfigWithLocalOverrides(GlobalConfigClass.Nested.class);
+ }
- @Test
- void globalConfigWithCommentPrefixAndLocalOverrides() throws Exception {
- Class> testClass = GlobalConfigWithPrefixClass.class;
+ @Test
+ void globalConfigWithCommentPrefixAndLocalOverrides() throws Exception {
+ assertGlobalConfigWithCommentPrefixAndLocalOverrides(GlobalConfigWithPrefixClass.Nested.class);
+ }
- Method method = testClass.getMethod("commentPrefixesOverrideCommentPrefix");
- SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
- MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, testClass);
-
- assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("#", "@"));
-
- method = testClass.getMethod("commentPrefixOverridesCommentPrefix");
- localSqlConfig = method.getAnnotation(Sql.class).config();
- cfg = new MergedSqlConfig(localSqlConfig, testClass);
-
- assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("#"));
- }
-
- @Test
- void globalConfigWithCommentPrefixesAndLocalOverrides() throws Exception {
- Class> testClass = GlobalConfigWithPrefixesClass.class;
-
- Method method = testClass.getMethod("commentPrefixesOverrideCommentPrefixes");
- SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
- MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, testClass);
-
- assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("#", "@"));
-
- method = testClass.getMethod("commentPrefixOverridesCommentPrefixes");
- localSqlConfig = method.getAnnotation(Sql.class).config();
- cfg = new MergedSqlConfig(localSqlConfig, testClass);
-
- assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("#"));
+ @Test
+ void globalConfigWithCommentPrefixesAndLocalOverrides() throws Exception {
+ assertGlobalConfigWithCommentPrefixesAndLocalOverrides(GlobalConfigWithPrefixesClass.Nested.class);
+ }
}
private void assertDefaults(MergedSqlConfig cfg) {
@@ -279,6 +256,79 @@ class MergedSqlConfigTests {
});
}
+ private void assertGlobalConfigWithDefaults(Class> testClass) throws Exception {
+ Method method = testClass.getMethod("globalConfigMethod");
+ SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
+ MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, testClass);
+ assertDefaults(cfg);
+ }
+
+ private void assertGlobalConfig(Class> testClass) throws NoSuchMethodException {
+ Method method = testClass.getMethod("globalConfigMethod");
+ SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
+ MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, testClass);
+
+ assertSoftly(softly -> {
+ softly.assertThat(cfg).isNotNull();
+ softly.assertThat(cfg.getDataSource()).as("dataSource").isEqualTo("");
+ softly.assertThat(cfg.getTransactionManager()).as("transactionManager").isEqualTo("");
+ softly.assertThat(cfg.getTransactionMode()).as("transactionMode").isEqualTo(INFERRED);
+ softly.assertThat(cfg.getEncoding()).as("encoding").isEqualTo("global");
+ softly.assertThat(cfg.getSeparator()).as("separator").isEqualTo("\n");
+ softly.assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("`", "--"));
+ softly.assertThat(cfg.getBlockCommentStartDelimiter()).as("blockCommentStartDelimiter").isEqualTo(DEFAULT_BLOCK_COMMENT_START_DELIMITER);
+ softly.assertThat(cfg.getBlockCommentEndDelimiter()).as("blockCommentEndDelimiter").isEqualTo(DEFAULT_BLOCK_COMMENT_END_DELIMITER);
+ softly.assertThat(cfg.getErrorMode()).as("errorMode").isEqualTo(IGNORE_FAILED_DROPS);
+ });
+ }
+
+ private void assertGlobalConfigWithLocalOverrides(Class> testClass) throws Exception {
+ Method method = testClass.getMethod("globalConfigWithLocalOverridesMethod");
+ SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
+ MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, testClass);
+
+ assertSoftly(softly -> {
+ softly.assertThat(cfg).isNotNull();
+ softly.assertThat(cfg.getDataSource()).as("dataSource").isEqualTo("");
+ softly.assertThat(cfg.getTransactionManager()).as("transactionManager").isEqualTo("");
+ softly.assertThat(cfg.getTransactionMode()).as("transactionMode").isEqualTo(INFERRED);
+ softly.assertThat(cfg.getEncoding()).as("encoding").isEqualTo("local");
+ softly.assertThat(cfg.getSeparator()).as("separator").isEqualTo("@@");
+ softly.assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("#"));
+ softly.assertThat(cfg.getBlockCommentStartDelimiter()).as("blockCommentStartDelimiter").isEqualTo(DEFAULT_BLOCK_COMMENT_START_DELIMITER);
+ softly.assertThat(cfg.getBlockCommentEndDelimiter()).as("blockCommentEndDelimiter").isEqualTo(DEFAULT_BLOCK_COMMENT_END_DELIMITER);
+ softly.assertThat(cfg.getErrorMode()).as("errorMode").isEqualTo(CONTINUE_ON_ERROR);
+ });
+ }
+
+ private void assertGlobalConfigWithCommentPrefixAndLocalOverrides(Class> testClass) throws Exception {
+ Method method = testClass.getMethod("commentPrefixesOverrideCommentPrefix");
+ SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
+ MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, testClass);
+
+ assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("#", "@"));
+
+ method = testClass.getMethod("commentPrefixOverridesCommentPrefix");
+ localSqlConfig = method.getAnnotation(Sql.class).config();
+ cfg = new MergedSqlConfig(localSqlConfig, testClass);
+
+ assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("#"));
+ }
+
+ private void assertGlobalConfigWithCommentPrefixesAndLocalOverrides(Class> testClass) throws Exception {
+ Method method = testClass.getMethod("commentPrefixesOverrideCommentPrefixes");
+ SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
+ MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, testClass);
+
+ assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("#", "@"));
+
+ method = testClass.getMethod("commentPrefixOverridesCommentPrefixes");
+ localSqlConfig = method.getAnnotation(Sql.class).config();
+ cfg = new MergedSqlConfig(localSqlConfig, testClass);
+
+ assertThat(cfg.getCommentPrefixes()).as("commentPrefixes").isEqualTo(array("#"));
+ }
+
private static String[] array(String... elements) {
return elements;
}
@@ -339,6 +389,13 @@ class MergedSqlConfigTests {
@Sql
public void globalConfigMethod() {
}
+
+ class Nested {
+
+ @Sql
+ public void globalConfigMethod() {
+ }
+ }
}
@SqlConfig(encoding = "global", separator = "\n", commentPrefixes = { "`", "--" }, errorMode = IGNORE_FAILED_DROPS)
@@ -351,6 +408,17 @@ class MergedSqlConfigTests {
@Sql(config = @SqlConfig(encoding = "local", separator = "@@", commentPrefix = "#", errorMode = CONTINUE_ON_ERROR))
public void globalConfigWithLocalOverridesMethod() {
}
+
+ class Nested {
+
+ @Sql
+ public void globalConfigMethod() {
+ }
+
+ @Sql(config = @SqlConfig(encoding = "local", separator = "@@", commentPrefix = "#", errorMode = CONTINUE_ON_ERROR))
+ public void globalConfigWithLocalOverridesMethod() {
+ }
+ }
}
@SqlConfig(commentPrefix = "`")
@@ -363,6 +431,17 @@ class MergedSqlConfigTests {
@Sql(config = @SqlConfig(commentPrefix = "#"))
public void commentPrefixOverridesCommentPrefix() {
}
+
+ class Nested {
+
+ @Sql(config = @SqlConfig(commentPrefixes = { "#", "@" }))
+ public void commentPrefixesOverrideCommentPrefix() {
+ }
+
+ @Sql(config = @SqlConfig(commentPrefix = "#"))
+ public void commentPrefixOverridesCommentPrefix() {
+ }
+ }
}
@SqlConfig(commentPrefixes = { "`", "--" })
@@ -375,6 +454,17 @@ class MergedSqlConfigTests {
@Sql(config = @SqlConfig(commentPrefix = "#"))
public void commentPrefixOverridesCommentPrefixes() {
}
+
+ class Nested {
+
+ @Sql(config = @SqlConfig(commentPrefixes = { "#", "@" }))
+ public void commentPrefixesOverrideCommentPrefixes() {
+ }
+
+ @Sql(config = @SqlConfig(commentPrefix = "#"))
+ public void commentPrefixOverridesCommentPrefixes() {
+ }
+ }
}
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/RepeatableSqlAnnotationSqlScriptsChildTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/RepeatableSqlAnnotationSqlScriptsChildTests.java
new file mode 100644
index 0000000000..931cd748ef
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/RepeatableSqlAnnotationSqlScriptsChildTests.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2002-2020 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.Inherited;
+import java.lang.annotation.Repeatable;
+
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Subclass of {@link RepeatableSqlAnnotationSqlScriptsParentTests} which verifies
+ * that {@link Repeatable} {@link Sql @Sql} annotations are not
+ * {@linkplain Inherited @Inherited} from a superclass if the subclass has local
+ * {@code @Sql} declarations.
+ *
+ * @author Sam Brannen
+ * @since 5.3
+ */
+@Sql("schema.sql")
+@Sql("data-add-catbert.sql")
+class RepeatableSqlAnnotationSqlScriptsChildTests extends RepeatableSqlAnnotationSqlScriptsParentTests {
+
+ @Test
+ @Order(1)
+ @Override
+ void classLevelScripts() {
+ // Should not find Dilbert, since local @Sql declarations shadow @Sql
+ // declarations on a superclass. This is due to the fact that we use
+ // "get" semantics instead of "find" semantics when searching for @Sql.
+ assertUsers("Catbert");
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/RepeatableSqlAnnotationSqlScriptsTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/RepeatableSqlAnnotationSqlScriptsParentTests.java
similarity index 87%
rename from spring-test/src/test/java/org/springframework/test/context/jdbc/RepeatableSqlAnnotationSqlScriptsTests.java
rename to spring-test/src/test/java/org/springframework/test/context/jdbc/RepeatableSqlAnnotationSqlScriptsParentTests.java
index 2b3e9a52bb..d91aa638a4 100644
--- a/spring-test/src/test/java/org/springframework/test/context/jdbc/RepeatableSqlAnnotationSqlScriptsTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/RepeatableSqlAnnotationSqlScriptsParentTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2020 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.
@@ -38,12 +38,12 @@ import org.springframework.test.context.ContextConfiguration;
@Sql("schema.sql")
@Sql("data.sql")
@DirtiesContext
-class RepeatableSqlAnnotationSqlScriptsTests extends AbstractTransactionalTests {
+class RepeatableSqlAnnotationSqlScriptsParentTests extends AbstractTransactionalTests {
@Test
@Order(1)
void classLevelScripts() {
- assertNumUsers(1);
+ assertUsers("Dilbert");
}
@Test
@@ -51,9 +51,9 @@ class RepeatableSqlAnnotationSqlScriptsTests extends AbstractTransactionalTests
@Sql("schema.sql")
@Sql("data.sql")
@Sql("data-add-dogbert.sql")
- @Order(1)
+ @Order(2)
void methodLevelScripts() {
- assertNumUsers(2);
+ assertUsers("Dilbert", "Dogbert");
}
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/AbstractSqlMergeModeTests.java b/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/AbstractSqlMergeModeTests.java
index bed80c8ced..724e041136 100644
--- a/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/AbstractSqlMergeModeTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/jdbc/merging/AbstractSqlMergeModeTests.java
@@ -30,6 +30,6 @@ import org.springframework.test.context.jdbc.SqlMergeMode;
*/
@ContextConfiguration(classes = EmptyDatabaseConfig.class)
@DirtiesContext
-abstract class AbstractSqlMergeModeTests extends AbstractTransactionalTests {
+public abstract class AbstractSqlMergeModeTests extends AbstractTransactionalTests {
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/SqlScriptNestedTests.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/SqlScriptNestedTests.java
index 2dc297f2e1..37fcadf372 100644
--- a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/SqlScriptNestedTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/nested/SqlScriptNestedTests.java
@@ -23,8 +23,13 @@ import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.test.context.NestedTestConfiguration;
+import org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration;
import org.springframework.test.context.jdbc.PopulatedSchemaDatabaseConfig;
import org.springframework.test.context.jdbc.Sql;
+import org.springframework.test.context.jdbc.SqlMergeMode;
+import org.springframework.test.context.jdbc.SqlMergeMode.MergeMode;
+import org.springframework.test.context.jdbc.merging.AbstractSqlMergeModeTests;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.transaction.AfterTransaction;
@@ -33,6 +38,8 @@ import org.springframework.test.jdbc.JdbcTestUtils;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.test.context.jdbc.SqlMergeMode.MergeMode.MERGE;
+import static org.springframework.test.context.jdbc.SqlMergeMode.MergeMode.OVERRIDE;
/**
* Integration tests that verify support for {@link Nested @Nested} test classes in
@@ -69,9 +76,6 @@ class SqlScriptNestedTests {
@Nested
class NestedTests {
- @Autowired
- JdbcTemplate jdbcTemplate;
-
@BeforeTransaction
@AfterTransaction
void checkInitialDatabaseState() {
@@ -83,9 +87,72 @@ class SqlScriptNestedTests {
void nestedSqlScripts() {
assertThat(countRowsInTable("user")).isEqualTo(1);
}
+ }
- private int countRowsInTable(String tableName) {
- return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
+ @Nested
+ @NestedTestConfiguration(EnclosingConfiguration.OVERRIDE)
+ @Sql({
+ "/org/springframework/test/context/jdbc/recreate-schema.sql",
+ "/org/springframework/test/context/jdbc/data-add-catbert.sql"
+ })
+ class NestedSqlMergeModeTests extends AbstractSqlMergeModeTests {
+
+ @Nested
+ @NestedTestConfiguration(EnclosingConfiguration.INHERIT)
+ @SqlMergeMode(MergeMode.MERGE)
+ class NestedClassLevelMergeSqlMergeModeTests {
+
+ @Test
+ void classLevelScripts() {
+ assertUsers("Catbert");
+ }
+
+ @Test
+ @Sql("/org/springframework/test/context/jdbc/data-add-dogbert.sql")
+ void merged() {
+ assertUsers("Catbert", "Dogbert");
+ }
+
+ @Test
+ @Sql({
+ "/org/springframework/test/context/jdbc/recreate-schema.sql",
+ "/org/springframework/test/context/jdbc/data.sql",
+ "/org/springframework/test/context/jdbc/data-add-dogbert.sql",
+ "/org/springframework/test/context/jdbc/data-add-catbert.sql"
+ })
+ @SqlMergeMode(MergeMode.OVERRIDE)
+ void overridden() {
+ assertUsers("Dilbert", "Dogbert", "Catbert");
+ }
+ }
+
+ @Nested
+ @NestedTestConfiguration(EnclosingConfiguration.INHERIT)
+ @SqlMergeMode(OVERRIDE)
+ class ClassLevelOverrideSqlMergeModeTests {
+
+ @Test
+ void classLevelScripts() {
+ assertUsers("Catbert");
+ }
+
+ @Test
+ @Sql("/org/springframework/test/context/jdbc/data-add-dogbert.sql")
+ @SqlMergeMode(MERGE)
+ void merged() {
+ assertUsers("Catbert", "Dogbert");
+ }
+
+ @Test
+ @Sql({
+ "/org/springframework/test/context/jdbc/recreate-schema.sql",
+ "/org/springframework/test/context/jdbc/data.sql",
+ "/org/springframework/test/context/jdbc/data-add-dogbert.sql",
+ "/org/springframework/test/context/jdbc/data-add-catbert.sql"
+ })
+ void overridden() {
+ assertUsers("Dilbert", "Dogbert", "Catbert");
+ }
}
}
diff --git a/src/docs/asciidoc/testing.adoc b/src/docs/asciidoc/testing.adoc
index 69d78339bd..4c53b749bc 100644
--- a/src/docs/asciidoc/testing.adoc
+++ b/src/docs/asciidoc/testing.adoc
@@ -1882,6 +1882,9 @@ following annotations.
* <>
* <>
* <>
+* <>
+* <>
+* <>
* <>
NOTE: The use of `@NestedTestConfiguration` typically only makes sense in conjunction