Introduce @SqlConfig for common SQL script config

Prior to this commit, @Sql provided attributes for configuring the
syntax of the referenced SQL scripts as well as exception handling and
transaction behavior; however, such configuration could not be reused
across @Sql declarations thus requiring developers to copy-and-paste
common configuration and resulting in unnecessary code duplication.

This commit addresses this issue by introducing a new @SqlConfig
annotation that can be used to declare common, global configuration for
SQL scripts that can be reused within a test class hierarchy.

 - Introduced top-level @SqlConfig annotation and extracted
   common configuration attributes from @Sql.

 - @SqlConfig can be used at the class level for common, global config
   or via the new 'config' attribute of @Sql for local config.

 - Introduced MergedSqlConfig as a holder for the merged values from
   local and global @SqlConfig instances. MergedSqlConfig also contains
   the logic for overriding global configuration with local
   configuration.

 - Refactored all attributes of @SqlConfig to be either of type String
   or custom enums in order to support overriding. Empty Strings or
   DEFAULT enum values imply the use of a default or inherited value.

Issue: SPR-11896
This commit is contained in:
Sam Brannen
2014-07-05 12:22:53 +02:00
parent 4e25a14f6c
commit 628806e85b
11 changed files with 770 additions and 143 deletions

View File

@@ -25,7 +25,7 @@ import static org.junit.Assert.*;
/**
* Integration tests that verify support for custom SQL script syntax
* configured via {@link Sql @Sql}.
* configured via {@link SqlConfig @SqlConfig}.
*
* @author Sam Brannen
* @since 4.1
@@ -35,16 +35,9 @@ import static org.junit.Assert.*;
public class CustomScriptSyntaxSqlScriptsTests extends AbstractTransactionalJUnit4SpringContextTests {
@Test
@SqlGroup({//
@Sql("schema.sql"),//
@Sql(//
scripts = "data-add-users-with-custom-script-syntax.sql",//
commentPrefix = "`",//
blockCommentStartDelimiter = "#$",//
blockCommentEndDelimiter = "$#",//
separator = "@@"//
) //
})
@Sql("schema.sql")
@Sql(scripts = "data-add-users-with-custom-script-syntax.sql",//
config = @SqlConfig(commentPrefix = "`", blockCommentStartDelimiter = "#$", blockCommentEndDelimiter = "$#", separator = "@@"))
public void methodLevelScripts() {
assertNumUsers(3);
}

View File

@@ -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 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.*;
/**
* Modified copy of {@link CustomScriptSyntaxSqlScriptsTests} with
* {@link SqlConfig @SqlConfig} defined at the class level.
*
* @author Sam Brannen
* @since 4.1
*/
@ContextConfiguration(classes = EmptyDatabaseConfig.class)
@DirtiesContext
@SqlConfig(commentPrefix = "`", blockCommentStartDelimiter = "#$", blockCommentEndDelimiter = "$#", separator = "@@")
public class GlobalCustomScriptSyntaxSqlScriptsTests extends AbstractTransactionalJUnit4SpringContextTests {
@Test
@Sql(scripts = "schema.sql", config = @SqlConfig(separator = ";"))
@Sql("data-add-users-with-custom-script-syntax.sql")
public void methodLevelScripts() {
assertNumUsers(3);
}
protected void assertNumUsers(int expected) {
assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user"));
}
}

View File

@@ -0,0 +1,177 @@
/*
* 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 org.junit.Test;
import static org.junit.Assert.*;
import static org.springframework.jdbc.datasource.init.ScriptUtils.*;
import static org.springframework.test.context.jdbc.SqlConfig.ErrorMode.*;
import static org.springframework.test.context.jdbc.SqlConfig.TransactionMode.*;
/**
* Unit tests for {@link MergedSqlConfig}.
*
* @author Sam Brannen
* @since 4.1
*/
public class MergedSqlConfigTests {
private void assertDefaults(MergedSqlConfig cfg) {
assertNotNull(cfg);
assertEquals("dataSource", "", cfg.getDataSource());
assertEquals("transactionManager", "", cfg.getTransactionManager());
assertEquals("transactionMode", INFERRED, cfg.getTransactionMode());
assertEquals("encoding", "", cfg.getEncoding());
assertEquals("separator", DEFAULT_STATEMENT_SEPARATOR, cfg.getSeparator());
assertEquals("commentPrefix", DEFAULT_COMMENT_PREFIX, cfg.getCommentPrefix());
assertEquals("blockCommentStartDelimiter", DEFAULT_BLOCK_COMMENT_START_DELIMITER,
cfg.getBlockCommentStartDelimiter());
assertEquals("blockCommentEndDelimiter", DEFAULT_BLOCK_COMMENT_END_DELIMITER, cfg.getBlockCommentEndDelimiter());
assertEquals("errorMode", FAIL_ON_ERROR, cfg.getErrorMode());
}
@Test
public void localConfigWithDefaults() throws Exception {
Method method = getClass().getMethod("localConfigMethodWithDefaults");
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, getClass());
assertDefaults(cfg);
}
@Test
public 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);
}
@Test
public void localConfigWithCustomValues() throws Exception {
Method method = getClass().getMethod("localConfigMethodWithCustomValues");
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, getClass());
assertNotNull(cfg);
assertEquals("dataSource", "ds", cfg.getDataSource());
assertEquals("transactionManager", "txMgr", cfg.getTransactionManager());
assertEquals("transactionMode", ISOLATED, cfg.getTransactionMode());
assertEquals("encoding", "enigma", cfg.getEncoding());
assertEquals("separator", "\n", cfg.getSeparator());
assertEquals("commentPrefix", "`", cfg.getCommentPrefix());
assertEquals("blockCommentStartDelimiter", "<<", cfg.getBlockCommentStartDelimiter());
assertEquals("blockCommentEndDelimiter", ">>", cfg.getBlockCommentEndDelimiter());
assertEquals("errorMode", IGNORE_FAILED_DROPS, cfg.getErrorMode());
}
@Test
public void localConfigWithContinueOnError() throws Exception {
Method method = getClass().getMethod("localConfigMethodWithContinueOnError");
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, getClass());
assertNotNull(cfg);
assertEquals("errorMode", CONTINUE_ON_ERROR, cfg.getErrorMode());
}
@Test
public void localConfigWithIgnoreFailedDrops() throws Exception {
Method method = getClass().getMethod("localConfigMethodWithIgnoreFailedDrops");
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, getClass());
assertNotNull(cfg);
assertEquals("errorMode", IGNORE_FAILED_DROPS, cfg.getErrorMode());
}
@Test
public void globalConfig() throws Exception {
Method method = GlobalConfigClass.class.getMethod("globalConfigMethod");
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, GlobalConfigClass.class);
assertNotNull(cfg);
assertEquals("dataSource", "", cfg.getDataSource());
assertEquals("transactionManager", "", cfg.getTransactionManager());
assertEquals("transactionMode", INFERRED, cfg.getTransactionMode());
assertEquals("encoding", "global", cfg.getEncoding());
assertEquals("separator", "\n", cfg.getSeparator());
assertEquals("commentPrefix", DEFAULT_COMMENT_PREFIX, cfg.getCommentPrefix());
assertEquals("blockCommentStartDelimiter", DEFAULT_BLOCK_COMMENT_START_DELIMITER,
cfg.getBlockCommentStartDelimiter());
assertEquals("blockCommentEndDelimiter", DEFAULT_BLOCK_COMMENT_END_DELIMITER, cfg.getBlockCommentEndDelimiter());
assertEquals("errorMode", IGNORE_FAILED_DROPS, cfg.getErrorMode());
}
@Test
public void globalConfigWithLocalOverrides() throws Exception {
Method method = GlobalConfigClass.class.getMethod("globalConfigWithLocalOverridesMethod");
SqlConfig localSqlConfig = method.getAnnotation(Sql.class).config();
MergedSqlConfig cfg = new MergedSqlConfig(localSqlConfig, GlobalConfigClass.class);
assertNotNull(cfg);
assertEquals("dataSource", "", cfg.getDataSource());
assertEquals("transactionManager", "", cfg.getTransactionManager());
assertEquals("transactionMode", INFERRED, cfg.getTransactionMode());
assertEquals("encoding", "local", cfg.getEncoding());
assertEquals("separator", "@@", cfg.getSeparator());
assertEquals("commentPrefix", DEFAULT_COMMENT_PREFIX, cfg.getCommentPrefix());
assertEquals("blockCommentStartDelimiter", DEFAULT_BLOCK_COMMENT_START_DELIMITER,
cfg.getBlockCommentStartDelimiter());
assertEquals("blockCommentEndDelimiter", DEFAULT_BLOCK_COMMENT_END_DELIMITER, cfg.getBlockCommentEndDelimiter());
assertEquals("errorMode", CONTINUE_ON_ERROR, cfg.getErrorMode());
}
// -------------------------------------------------------------------------
@Sql
public static void localConfigMethodWithDefaults() {
}
@Sql(config = @SqlConfig(dataSource = "ds", transactionManager = "txMgr", transactionMode = ISOLATED, encoding = "enigma", separator = "\n", commentPrefix = "`", blockCommentStartDelimiter = "<<", blockCommentEndDelimiter = ">>", errorMode = IGNORE_FAILED_DROPS))
public static void localConfigMethodWithCustomValues() {
}
@Sql(config = @SqlConfig(errorMode = CONTINUE_ON_ERROR))
public static void localConfigMethodWithContinueOnError() {
}
@Sql(config = @SqlConfig(errorMode = IGNORE_FAILED_DROPS))
public static void localConfigMethodWithIgnoreFailedDrops() {
}
@SqlConfig
public static class GlobalConfigWithDefaultsClass {
@Sql("foo.sql")
public void globalConfigMethod() {
}
}
@SqlConfig(encoding = "global", separator = "\n", errorMode = IGNORE_FAILED_DROPS)
public static class GlobalConfigClass {
@Sql("foo.sql")
public void globalConfigMethod() {
}
@Sql(scripts = "foo.sql", config = @SqlConfig(encoding = "local", separator = "@@", errorMode = CONTINUE_ON_ERROR))
public void globalConfigWithLocalOverridesMethod() {
}
}
}

View File

@@ -40,6 +40,7 @@ import static org.junit.Assert.*;
/**
* Integration tests for {@link Sql @Sql} that verify support for multiple
* {@link DataSource}s and {@link PlatformTransactionManager}s.
* <p>Simultaneously tests for method-level overrides via {@code @SqlConfig}.
*
* @author Sam Brannen
* @since 4.1
@@ -48,6 +49,7 @@ import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@DirtiesContext
@SqlConfig(dataSource = "dataSource1", transactionManager = "txMgr1")
public class MultipleDataSourcesAndTransactionManagersSqlScriptsTests {
@Autowired
@@ -58,13 +60,13 @@ public class MultipleDataSourcesAndTransactionManagersSqlScriptsTests {
@Test
@Sql(scripts = "data-add-dogbert.sql", dataSource = "dataSource1", transactionManager = "txMgr1")
@Sql("data-add-dogbert.sql")
public void database1() {
assertUsers(new JdbcTemplate(dataSource1), "Dilbert", "Dogbert");
}
@Test
@Sql(scripts = "data-add-catbert.sql", dataSource = "dataSource2", transactionManager = "txMgr2")
@Sql(scripts = "data-add-catbert.sql", config = @SqlConfig(dataSource = "dataSource2", transactionManager = "txMgr2"))
public void database2() {
assertUsers(new JdbcTemplate(dataSource2), "Dilbert", "Catbert");
}

View File

@@ -48,6 +48,8 @@ import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@DirtiesContext
@Transactional("txMgr1")
@SqlConfig(dataSource = "dataSource1", transactionManager = "txMgr1")
public class MultipleDataSourcesAndTransactionManagersTransactionalSqlScriptsTests {
@Autowired
@@ -58,15 +60,14 @@ public class MultipleDataSourcesAndTransactionManagersTransactionalSqlScriptsTes
@Test
@Transactional("txMgr1")
@Sql(scripts = "data-add-dogbert.sql", dataSource = "dataSource1", transactionManager = "txMgr1")
@Sql("data-add-dogbert.sql")
public void database1() {
assertUsers(new JdbcTemplate(dataSource1), "Dilbert", "Dogbert");
}
@Test
@Transactional("txMgr2")
@Sql(scripts = "data-add-catbert.sql", dataSource = "dataSource2", transactionManager = "txMgr2")
@Sql(scripts = "data-add-catbert.sql", config = @SqlConfig(dataSource = "dataSource2", transactionManager = "txMgr2"))
public void database2() {
assertUsers(new JdbcTemplate(dataSource2), "Dilbert", "Catbert");
}

View File

@@ -19,6 +19,7 @@ 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.jdbc.SqlConfig.TransactionMode;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
@@ -27,7 +28,7 @@ import static org.junit.Assert.*;
/**
* Transactional integration tests that verify commit semantics for
* {@link Sql#requireNewTransaction}.
* {@link SqlConfig#requireNewTransaction}.
*
* @author Sam Brannen
* @since 4.1
@@ -42,7 +43,7 @@ public class RequiresNewTransactionSqlScriptsTests extends AbstractTransactional
}
@Test
@SqlGroup(@Sql(scripts = "data-add-dogbert.sql", requireNewTransaction = true))
@SqlGroup(@Sql(scripts = "data-add-dogbert.sql", config = @SqlConfig(transactionMode = TransactionMode.ISOLATED)))
public void methodLevelScripts() {
assertNumUsers(1);
}

View File

@@ -62,7 +62,7 @@ public class TransactionalAfterTestMethodSqlScriptsTests extends AbstractTransac
@Test
@SqlGroup({//
@Sql({ "schema.sql", "data.sql" }),//
@Sql({ "schema.sql", "data.sql" }),//
@Sql(scripts = "drop-schema.sql", executionPhase = AFTER_TEST_METHOD) //
})
// test## is required for @FixMethodOrder.