此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
执行 SQL 脚本
在针对关系数据库编写集成测试时,通常
运行 SQL 脚本以修改数据库架构或将测试数据插入表中。该模块支持初始化嵌入式或现有数据库
通过在加载 Spring 时执行 SQL 脚本。有关详细信息,请参阅嵌入式数据库支持和使用嵌入式数据库测试数据访问逻辑。spring-jdbc
ApplicationContext
尽管在加载时初始化数据库以进行测试一次非常有用,但有时必须能够修改
数据库。以下部分介绍如何运行 SQL
在集成测试期间以编程和声明方式编写脚本。ApplicationContext
以编程方式执行 SQL 脚本
Spring 提供了以下选项,用于在 集成测试方法。
-
org.springframework.jdbc.datasource.init.ScriptUtils
-
org.springframework.jdbc.datasource.init.ResourceDatabasePopulator
-
org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests
-
org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests
ScriptUtils
提供用于处理 SQL 的静态实用程序方法的集合
脚本,主要供框架内部使用。但是,如果您
需要完全控制 SQL 脚本的解析和运行方式,可能适合
您的需求比后面描述的其他一些替代方案更好。请参阅个人的 javadoc
方法中的更多详细信息。ScriptUtils
ScriptUtils
ResourceDatabasePopulator
提供基于对象的 API,用于以编程方式填充,
使用外部
资源。 提供用于配置字符的选项
encoding、语句分隔符、注释分隔符和错误处理标志
解析并运行脚本。每个配置选项都有一个合理的
default 值。请参阅 javadoc 以获取
有关默认值的详细信息。要运行在 中配置的脚本,可以调用
针对 a 或
对 .以下示例
为测试架构和测试数据指定 SQL 脚本,将语句分隔符设置为 ,然后针对 :ResourceDatabasePopulator
ResourceDatabasePopulator
populate(Connection)
java.sql.Connection
execute(DataSource)
javax.sql.DataSource
@@
DataSource
-
Java
-
Kotlin
@Test
void databaseTest() {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScripts(
new ClassPathResource("test-schema.sql"),
new ClassPathResource("test-data.sql"));
populator.setSeparator("@@");
populator.execute(this.dataSource);
// run code that uses the test schema and data
}
@Test
fun databaseTest() {
val populator = ResourceDatabasePopulator()
populator.addScripts(
ClassPathResource("test-schema.sql"),
ClassPathResource("test-data.sql"))
populator.setSeparator("@@")
populator.execute(dataSource)
// run code that uses the test schema and data
}
请注意,内部委托 to 进行解析
以及运行 SQL 脚本。类似地,AbstractTransactionalJUnit4SpringContextTests
和AbstractTransactionalTestNGSpringContextTests
中的方法在内部使用a来运行 SQL 脚本。请参阅 Javadoc 以获取
各种方法了解更多详情。ResourceDatabasePopulator
ScriptUtils
executeSqlScript(..)
ResourceDatabasePopulator
executeSqlScript(..)
使用 @Sql 以声明方式执行 SQL 脚本
除了上述以编程方式运行 SQL 脚本的机制外,
你可以在 Spring TestContext Framework 中以声明方式配置 SQL 脚本。
具体来说,您可以在测试类或测试方法上声明注释,以
配置单个 SQL 语句或 SQL 脚本的资源路径,这些脚本应该是
在集成测试类或测试方法之前或之后针对给定数据库运行。
支持由 提供,已启用
默认情况下。@Sql
@Sql
SqlScriptsTestExecutionListener
默认情况下,方法级声明会覆盖类级声明,但 this
可以通过 按测试类或按测试方法配置行为。有关更多详细信息,请参阅使用 但是,这不适用于为 or 执行阶段配置的类级声明。此类声明不能
overridden,对应的脚本和语句将为每个类执行一次
以及任何方法级脚本和语句。 |
路径资源语义
每个 path 都解释为 Spring 。普通路径(例如 )被视为相对于
定义测试类。以斜杠开头的路径被视为绝对路径
类路径资源(例如 )。引用
URL(例如,前缀为 , , 的路径)是使用
指定的资源协议。Resource
"schema.sql"
"/org/example/schema.sql"
classpath:
file:
http:
以下示例演示如何在类级别和方法级别使用
在基于 JUnit Jupiter 的集成测试类中:@Sql
-
Java
-
Kotlin
@SpringJUnitConfig
@Sql("/test-schema.sql")
class DatabaseTests {
@Test
void emptySchemaTest() {
// run code that uses the test schema without any test data
}
@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})
void userTest() {
// run code that uses the test schema and test data
}
}
@SpringJUnitConfig
@Sql("/test-schema.sql")
class DatabaseTests {
@Test
fun emptySchemaTest() {
// run code that uses the test schema without any test data
}
@Test
@Sql("/test-schema.sql", "/test-user-data.sql")
fun userTest() {
// run code that uses the test schema and test data
}
}
默认脚本检测
如果未指定 SQL 脚本或语句,则尝试检测脚本,具体取决于声明的位置。如果无法检测到 default,则抛出 an。default
@Sql
IllegalStateException
-
类级声明:如果带注解的测试类是 ,则 相应的默认脚本为 。
com.example.MyTest
classpath:com/example/MyTest.sql
-
方法级声明:如果带注释的测试方法已命名且为 在 类 中定义,对应的默认脚本为 。
testMethod()
com.example.MyTest
classpath:com/example/MyTest.testMethod.sql
记录 SQL 脚本和语句
如果要查看正在执行的 SQL 脚本,请将日志记录类别设置为 。org.springframework.test.context.jdbc
DEBUG
如果要查看正在执行的 SQL 语句,请将日志记录类别设置为 。org.springframework.jdbc.datasource.init
DEBUG
声明多个集@Sql
如果需要为给定的测试类或测试配置多组 SQL 脚本
方法,但具有不同的语法配置、不同的错误处理规则,或者
每个集合的不同执行阶段,您可以声明多个 .您可以
用作可重复的注释,也可以使用注释
作为显式容器来声明 的多个实例。@Sql
@Sql
@SqlGroup
@Sql
以下示例显示了如何用作可重复的注释:@Sql
-
Java
-
Kotlin
@Test
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`"))
@Sql("/test-user-data.sql")
void userTest() {
// run code that uses the test schema and test data
}
@Test
@Sql("/test-schema.sql", config = SqlConfig(commentPrefix = "`"))
@Sql("/test-user-data.sql")
fun userTest() {
// run code that uses the test schema and test data
}
在前面示例所示的场景中,该脚本使用
单行注释的不同语法。test-schema.sql
以下示例与前面的示例相同,只是声明在 .使用 of 是可选的,
但您可能需要 用于与其他 JVM 语言兼容。@Sql
@SqlGroup
@SqlGroup
@SqlGroup
-
Java
-
Kotlin
@Test
@SqlGroup({
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
@Sql("/test-user-data.sql")
)}
void userTest() {
// run code that uses the test schema and test data
}
@Test
@SqlGroup(
Sql("/test-schema.sql", config = SqlConfig(commentPrefix = "`")),
Sql("/test-user-data.sql")
)
fun userTest() {
// Run code that uses the test schema and test data
}
脚本执行阶段
默认情况下,SQL 脚本在相应的测试方法之前运行。但是,如果您
需要在测试方法之后运行一组特定的脚本(例如,为了清理
database state)中,可以将 中的属性设置为 ,如下例所示:executionPhase
@Sql
AFTER_TEST_METHOD
-
Java
-
Kotlin
@Test
@Sql(
scripts = "create-test-data.sql",
config = @SqlConfig(transactionMode = ISOLATED)
)
@Sql(
scripts = "delete-test-data.sql",
config = @SqlConfig(transactionMode = ISOLATED),
executionPhase = AFTER_TEST_METHOD
)
void userTest() {
// run code that needs the test data to be committed
// to the database outside of the test's transaction
}
@Test
@Sql("create-test-data.sql",
config = SqlConfig(transactionMode = ISOLATED))
@Sql("delete-test-data.sql",
config = SqlConfig(transactionMode = ISOLATED),
executionPhase = AFTER_TEST_METHOD)
fun userTest() {
// run code that needs the test data to be committed
// to the database outside of the test's transaction
}
ISOLATED 和 分别从 和 静态导入。AFTER_TEST_METHOD Sql.TransactionMode Sql.ExecutionPhase |
从 Spring Framework 6.1 开始,可以在
通过将类级声明中的属性设置为 或 ,在 Test 类之后,如下例所示:executionPhase
@Sql
BEFORE_TEST_CLASS
AFTER_TEST_CLASS
-
Java
-
Kotlin
@SpringJUnitConfig
@Sql(scripts = "/test-schema.sql", executionPhase = BEFORE_TEST_CLASS)
class DatabaseTests {
@Test
void emptySchemaTest() {
// run code that uses the test schema without any test data
}
@Test
@Sql("/test-user-data.sql")
void userTest() {
// run code that uses the test schema and test data
}
}
@SpringJUnitConfig
@Sql("/test-schema.sql", executionPhase = BEFORE_TEST_CLASS)
class DatabaseTests {
@Test
fun emptySchemaTest() {
// run code that uses the test schema without any test data
}
@Test
@Sql("/test-user-data.sql")
fun userTest() {
// run code that uses the test schema and test data
}
}
BEFORE_TEST_CLASS 是从 静态导入的。Sql.ExecutionPhase |
Script Configuration 替换为@SqlConfig
您可以使用注释配置脚本解析和错误处理。
当声明为集成测试类上的类级注释时,用作测试类层次结构中所有 SQL 脚本的全局配置。什么时候
使用 Comments 的属性直接声明,用作在封闭 Comments 中声明的 SQL 脚本的本地配置。中的每个属性都有一个隐式默认值,即
记录在相应属性的 javadoc 中。由于为
annotation 属性,遗憾的是,它不是
可以将值 of 分配给 Annotation 属性。因此,为了
支持覆盖继承的全局配置,属性具有
显式默认值 (for Strings)、(for Arrays) 或 (for
枚举)。这种方法允许 的本地声明选择性地覆盖
通过提供值 Other 来自全局声明的单个属性
than 、 或 .全局属性在何时继承
local 属性不提供除 、 、 或 以外的显式值。因此,显式本地配置将覆盖全局配置。@SqlConfig
@SqlConfig
config
@Sql
@SqlConfig
@Sql
@SqlConfig
null
@SqlConfig
""
{}
DEFAULT
@SqlConfig
@SqlConfig
""
{}
DEFAULT
@SqlConfig
@SqlConfig
""
{}
DEFAULT
提供的配置选项与 和 等效
supported by and but 是这些
由 XML 命名空间元素提供。请参阅 javadoc
有关详细信息,请参阅 @Sql
和 @SqlConfig
中的 individual attributes。@Sql
@SqlConfig
ScriptUtils
ResourceDatabasePopulator
<jdbc:initialize-database/>
事务管理@Sql
默认情况下,会推断出所需的事务
使用 配置的脚本的语义。具体来说,SQL 脚本是运行的
如果没有事务,则在现有的 Spring 管理的事务中(例如,
事务 管理,或者管理在隔离的事务中,具体取决于配置的值
的属性 in 和测试的 .作为最低限度,
但是,A 必须存在于测试的 .SqlScriptsTestExecutionListener
@Sql
TransactionalTestExecutionListener
@Transactional
transactionMode
@SqlConfig
PlatformTransactionManager
ApplicationContext
javax.sql.DataSource
ApplicationContext
如果 用于检测 和 并推断交易语义的算法不符合您的需求,
您可以通过设置 的 and 属性来指定显式名称。此外,您可以控制事务传播
行为(例如,是否
脚本应在隔离的事务中运行)。虽然对所有
事务管理支持的选项 is not the scope of this
参考手册中,@SqlConfig
和 SqlScriptsTestExecutionListener
的 javadoc 提供了详细信息,以下示例显示了一个典型的测试场景
它使用 JUnit Jupiter 和事务测试:SqlScriptsTestExecutionListener
DataSource
PlatformTransactionManager
dataSource
transactionManager
@SqlConfig
transactionMode
@SqlConfig
@Sql
@Sql
-
Java
-
Kotlin
@SpringJUnitConfig(TestDatabaseConfig.class)
@Transactional
class TransactionalSqlScriptsTests {
final JdbcTemplate jdbcTemplate;
@Autowired
TransactionalSqlScriptsTests(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
@Sql("/test-data.sql")
void usersTest() {
// verify state in test database:
assertNumUsers(2);
// run code that uses the test data...
}
int countRowsInTable(String tableName) {
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
}
void assertNumUsers(int expected) {
assertEquals(expected, countRowsInTable("user"),
"Number of rows in the [user] table.");
}
}
@SpringJUnitConfig(TestDatabaseConfig::class)
@Transactional
class TransactionalSqlScriptsTests @Autowired constructor(dataSource: DataSource) {
val jdbcTemplate: JdbcTemplate = JdbcTemplate(dataSource)
@Test
@Sql("/test-data.sql")
fun usersTest() {
// verify state in test database:
assertNumUsers(2)
// run code that uses the test data...
}
fun countRowsInTable(tableName: String): Int {
return JdbcTestUtils.countRowsInTable(jdbcTemplate, tableName)
}
fun assertNumUsers(expected: Int) {
assertEquals(expected, countRowsInTable("user"),
"Number of rows in the [user] table.")
}
}
合并和覆盖配置@SqlMergeMode
从 Spring Framework 5.2 开始,可以将方法级声明与
类级声明。例如,这允许您为
数据库架构或一些常见的测试数据,然后提供额外的
每个测试方法的用例特定测试数据。要启用合并,请注释
您的 test 类或 test 方法与 .要禁用
Specific Test Method(或Specific Test Subclass),可以切换回默认模式
通过。请参阅 @SqlMergeMode
注释文档部分 有关示例和更多详细信息。@Sql
@Sql
@SqlMergeMode(MERGE)
@SqlMergeMode(OVERRIDE)