此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
事务管理
在 TestContext 框架中,事务由TransactionalTestExecutionListener
,默认情况下配置,即使您没有
显式声明@TestExecutionListeners
在测试类上。启用对
事务,但是,您必须配置PlatformTransactionManager
bean 中的ApplicationContext
加载了@ContextConfiguration
语义(进一步
详细信息将在后面提供)。此外,您必须声明 Spring 的@Transactional
在类或方法级别进行测试的注释。
测试管理的事务
测试管理的事务是通过使用TransactionalTestExecutionListener
或以编程方式使用TestTransaction
(后述)。您不应将此类事务与 Spring 管理的事务混淆
事务(由 Spring 在ApplicationContext
加载
测试)或应用程序管理的事务(在
测试调用的应用程序代码)。Spring 管理和应用程序管理
事务通常参与测试管理的事务。但是,您应该使用
如果 Spring 管理或应用程序管理的事务配置了任何
传播类型以外的REQUIRED
或SUPPORTS
(有关详细信息,请参阅有关事务传播的讨论)。
抢占超时和测试管理的事务
使用测试框架中任何形式的抢占式超时时必须小心 与 Spring 的测试管理事务结合使用。 具体来说,Spring 的测试支持将事务状态绑定到当前线程(通过
一个 可能发生这种情况的情况包括但不限于以下情况。
|
启用和禁用事务
使用@Transactional
导致测试在
事务,默认情况下,在测试完成后自动回滚。
如果测试类的注释为@Transactional
,则该类中的每个测试方法
层次结构在事务中运行。未注释的测试方法@Transactional
(在类或方法级别)不在事务中运行。注意
那@Transactional
测试生命周期方法(例如,方法
用 JUnit Jupiter 的@BeforeAll
,@BeforeEach
等。此外,测试
用@Transactional
但有propagation
属性设置为NOT_SUPPORTED
或NEVER
不在事务中运行。
属性 | 支持测试管理的事务 |
---|---|
|
是的 |
|
只 |
|
不 |
|
不 |
|
不 |
|
否:使用 |
|
否:使用 |
方法级生命周期方法——例如,用 JUnit Jupiter 的 如果您需要在
transaction,不妨注入一个对应的 |
请注意AbstractTransactionalJUnit4SpringContextTests
和AbstractTransactionalTestNGSpringContextTests
在类级别预先配置为事务支持。
以下示例演示了编写集成测试的常见场景
基于休眠的UserRepository
:
-
Java
-
Kotlin
@SpringJUnitConfig(TestConfig.class)
@Transactional
class HibernateUserRepositoryTests {
@Autowired
HibernateUserRepository repository;
@Autowired
SessionFactory sessionFactory;
JdbcTemplate jdbcTemplate;
@Autowired
void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
void createUser() {
// track initial state in test database:
final int count = countRowsInTable("user");
User user = new User(...);
repository.save(user);
// Manual flush is required to avoid false positive in test
sessionFactory.getCurrentSession().flush();
assertNumUsers(count + 1);
}
private int countRowsInTable(String tableName) {
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
}
private void assertNumUsers(int expected) {
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));
}
}
@SpringJUnitConfig(TestConfig::class)
@Transactional
class HibernateUserRepositoryTests {
@Autowired
lateinit var repository: HibernateUserRepository
@Autowired
lateinit var sessionFactory: SessionFactory
lateinit var jdbcTemplate: JdbcTemplate
@Autowired
fun setDataSource(dataSource: DataSource) {
this.jdbcTemplate = JdbcTemplate(dataSource)
}
@Test
fun createUser() {
// track initial state in test database:
val count = countRowsInTable("user")
val user = User()
repository.save(user)
// Manual flush is required to avoid false positive in test
sessionFactory.getCurrentSession().flush()
assertNumUsers(count + 1)
}
private fun countRowsInTable(tableName: String): Int {
return JdbcTestUtils.countRowsInTable(jdbcTemplate, tableName)
}
private fun assertNumUsers(expected: Int) {
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"))
}
}
如事务回滚和提交行为中所述,
在createUser()
方法运行,
由于对数据库所做的任何更改都会自动回滚TransactionalTestExecutionListener
.
事务回滚和提交行为
默认情况下,测试事务将在完成
测试;但是,事务提交和回滚行为可以声明性地配置
通过@Commit
和@Rollback
附注。有关更多详细信息,请参阅注释支持部分中的相应条目。
程序化事务管理
您可以使用静态
方法TestTransaction
.例如,您可以使用TestTransaction
测试中
方法、before methods 和 after 方法来启动或结束当前测试管理
事务或配置当前测试管理事务以进行回滚或提交。
对TestTransaction
只要TransactionalTestExecutionListener
已启用。
以下示例演示了TestTransaction
.请参阅
javadoc 的TestTransaction
了解更多详情。
-
Java
-
Kotlin
@ContextConfiguration(classes = TestConfig.class)
public class ProgrammaticTransactionManagementTests extends
AbstractTransactionalJUnit4SpringContextTests {
@Test
public void transactionalTest() {
// assert initial state in test database:
assertNumUsers(2);
deleteFromTables("user");
// changes to the database will be committed!
TestTransaction.flagForCommit();
TestTransaction.end();
assertFalse(TestTransaction.isActive());
assertNumUsers(0);
TestTransaction.start();
// perform other actions against the database that will
// be automatically rolled back after the test completes...
}
protected void assertNumUsers(int expected) {
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));
}
}
@ContextConfiguration(classes = [TestConfig::class])
class ProgrammaticTransactionManagementTests : AbstractTransactionalJUnit4SpringContextTests() {
@Test
fun transactionalTest() {
// assert initial state in test database:
assertNumUsers(2)
deleteFromTables("user")
// changes to the database will be committed!
TestTransaction.flagForCommit()
TestTransaction.end()
assertFalse(TestTransaction.isActive())
assertNumUsers(0)
TestTransaction.start()
// perform other actions against the database that will
// be automatically rolled back after the test completes...
}
protected fun assertNumUsers(expected: Int) {
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"))
}
}
在事务之外运行代码
有时,可能需要在事务测试之前或之后运行某些代码
方法,但要在事务上下文之外——例如,验证初始
运行测试之前的数据库状态或验证预期的事务提交
测试运行后的行为(如果测试配置为提交事务)。TransactionalTestExecutionListener
支持@BeforeTransaction
和@AfterTransaction
注解。您可以注释任何void
测试类中的方法或任何void
默认方法,在测试接口中使用其中之一
注释,以及TransactionalTestExecutionListener
确保您的
before-transaction 方法或 after-transaction 方法在适当的时间运行。
一般而言 但是,从 Spring Framework 6.1 开始,对于使用
|
任何之前的方法(例如用 JUnit Jupiter 的 同样,用 |
配置事务管理器
TransactionalTestExecutionListener
期望PlatformTransactionManager
豆子要成为
在 Spring 中定义ApplicationContext
用于测试。如果有多个实例
之PlatformTransactionManager
在测试的ApplicationContext
,您可以声明一个
限定符,使用@Transactional("myTxMgr")
或@Transactional(transactionManager =
"myTxMgr")
或TransactionManagementConfigurer
可由@Configuration
类。请查阅Java文档
为TestContextTransactionUtils.retrieveTransactionManager()
有关
用于在测试的ApplicationContext
.
所有与事务相关的注释的演示
以下基于 JUnit Jupiter 的示例显示了一个虚构的集成测试
突出显示所有与事务相关的注释的方案。该示例不是有意的
演示最佳实践,而是演示这些注释如何
使用。有关进一步的信息,请参阅注释支持部分
信息和配置示例。事务管理@Sql
包含一个使用@Sql
为
具有默认事务回滚语义的声明性 SQL 脚本执行。这
以下示例显示了相关注释:
-
Java
-
Kotlin
@SpringJUnitConfig
@Transactional(transactionManager = "txMgr")
@Commit
class FictitiousTransactionalTest {
@BeforeTransaction
void verifyInitialDatabaseState() {
// logic to verify the initial state before a transaction is started
}
@BeforeEach
void setUpTestDataWithinTransaction() {
// set up test data within the transaction
}
@Test
// overrides the class-level @Commit setting
@Rollback
void modifyDatabaseWithinTransaction() {
// logic which uses the test data and modifies database state
}
@AfterEach
void tearDownWithinTransaction() {
// run "tear down" logic within the transaction
}
@AfterTransaction
void verifyFinalDatabaseState() {
// logic to verify the final state after transaction has rolled back
}
}
@SpringJUnitConfig
@Transactional(transactionManager = "txMgr")
@Commit
class FictitiousTransactionalTest {
@BeforeTransaction
fun verifyInitialDatabaseState() {
// logic to verify the initial state before a transaction is started
}
@BeforeEach
fun setUpTestDataWithinTransaction() {
// set up test data within the transaction
}
@Test
// overrides the class-level @Commit setting
@Rollback
fun modifyDatabaseWithinTransaction() {
// logic which uses the test data and modifies database state
}
@AfterEach
fun tearDownWithinTransaction() {
// run "tear down" logic within the transaction
}
@AfterTransaction
fun verifyFinalDatabaseState() {
// logic to verify the final state after transaction has rolled back
}
}
测试 ORM 代码时避免误报
当您测试作 Hibernate 会话或 JPA 状态的应用程序代码时 持久性上下文,请确保刷新测试方法中的基础工作单元 运行该代码。未能刷新基础工作单元可能会产生错误 positives:您的测试通过了,但相同的代码在实时生产中抛出异常 环境。请注意,这适用于任何维护内存单元的 ORM 框架 工作。在以下基于 Hibernate 的示例测试用例中,一种方法演示了 误报,另一种方法正确暴露刷新 会期:
以下示例显示了 JPA 的匹配方法:
|
测试 ORM 实体生命周期回调
与测试 ORM 代码时避免误报的说明类似,如果您的应用程序使用实体生命周期回调( 称为实体侦听器),请确保刷新测试中的基础工作单元 运行该代码的方法。未能刷新或清除基础工作单元可能会 导致某些生命周期回调未被调用。 例如,当使用 JPA 时, 以下示例演示如何刷新
有关使用所有JPA生命周期回调的工作示例,请参阅Spring Framework测试套件中的JpaEntityListenerTests。 |