对于最新的稳定版本,请使用 Spring Framework 6.2.0spring-doc.cn

事务管理

在 TestContext 框架中,事务由 默认配置的 管理,即使您没有这样做 在你的测试类上显式声明。要启用对 transactions,但是,您必须在 中配置一个 bean,该 bean 加载了语义(进一步 详细信息将在后面提供)。此外,你必须在测试的类或方法级别声明 Spring 的注释。TransactionalTestExecutionListener@TestExecutionListenersPlatformTransactionManagerApplicationContext@ContextConfiguration@Transactionalspring-doc.cn

测试管理的事务

测试托管的事务是通过使用 或 using 以编程方式进行管理的事务(稍后介绍)。您不应将此类事务与 Spring 管理的事务混淆 事务(那些由 Spring 直接管理的 测试)或应用程序管理的事务(在 由测试调用的应用程序代码)。Spring 管理和应用程序管理 事务通常参与 test-managed 事务。但是,您应该使用 如果 Spring 管理的事务或应用程序管理的事务配置了任何 propagation type 而不是 or (有关详细信息,请参阅有关事务传播的讨论)。TransactionalTestExecutionListenerTestTransactionApplicationContextREQUIREDSUPPORTSspring-doc.cn

抢占式超时和测试托管事务

在测试框架中使用任何形式的抢占式超时时,必须小心 与 Spring 的 test-managed 事务结合使用。spring-doc.cn

具体来说, Spring 的测试支持将事务状态绑定到当前线程(通过 一个变量)在调用当前测试方法之前。如果 testing 框架在新线程中调用当前测试方法,以支持 preemptive timeout,则在当前测试方法中执行的任何操作都不会 在测试托管的事务中调用。因此,任何此类操作的结果 不会使用 test-managed 事务回滚。相反,此类操作 将提交到持久化存储(例如,关系数据库),甚至 尽管 Spring 正确回滚了 test-managed 事务。java.lang.ThreadLocalspring-doc.cn

可能发生这种情况的情况包括但不限于以下情况。spring-doc.cn

  • JUnit 4 的支持和规则@Test(timeout = …​)TimeOutspring-doc.cn

  • 类中的 JUnit Jupiter 方法assertTimeoutPreemptively(…​)org.junit.jupiter.api.Assertionsspring-doc.cn

  • TestNG 的支持@Test(timeOut = …​)spring-doc.cn

启用和禁用事务

为测试方法添加注释会导致测试在 transaction 的事务,默认情况下,在测试完成后自动回滚。 如果测试类使用 进行批注,则该类中的每个测试方法 hierarchy 在事务中运行。未注释的测试方法(在类或方法级别)不会在事务中运行。注意 这在测试生命周期方法(例如,方法)上不受支持 用 JUnit Jupiter 的 , , 等进行注释。此外,测试 被注释,但具有 set to 或 are not run 在事务中的属性。@Transactional@Transactional@Transactional@Transactional@BeforeAll@BeforeEach@TransactionalpropagationNOT_SUPPORTEDNEVERspring-doc.cn

表 1. 属性支持@Transactional
属性 支持测试托管事务

valuetransactionManagerspring-doc.cn

是的spring-doc.cn

propagationspring-doc.cn

only 和 are supportedPropagation.NOT_SUPPORTEDPropagation.NEVERspring-doc.cn

isolationspring-doc.cn

spring-doc.cn

timeoutspring-doc.cn

spring-doc.cn

readOnlyspring-doc.cn

spring-doc.cn

rollbackForrollbackForClassNamespring-doc.cn

否:改用TestTransaction.flagForRollback()spring-doc.cn

noRollbackFornoRollbackForClassNamespring-doc.cn

否:改用TestTransaction.flagForCommit()spring-doc.cn

方法级生命周期方法(例如,使用 JUnit Jupiter 的 or 注释的方法)在测试托管的事务中运行。另一方面 hand、套件级和类级生命周期方法,例如,带有 使用 TestNG 、 、 或 — 注释的 JUnit Jupiter 的 or 和 方法不在 test-managed 事务。@BeforeEach@AfterEach@BeforeAll@AfterAll@BeforeSuite@AfterSuite@BeforeClass@AfterClassspring-doc.cn

如果您需要在 transaction,您可能希望将相应的 您的 test 类,然后将其与 for programmatic 一起使用 事务管理。PlatformTransactionManagerTransactionTemplatespring-doc.cn

以下示例演示了为 编写集成测试的常见场景 基于 Hibernate 的 :UserRepositoryspring-doc.cn

@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"))
	}
}

Transaction Rollback 和 Commit Behavior 中所述,无需 在方法运行后清理数据库,因为对 数据库会自动回滚。createUser()TransactionalTestExecutionListenerspring-doc.cn

事务回滚和提交行为

默认情况下,测试事务将在完成 测试;但是,事务提交和回滚行为可以通过声明方式进行配置 通过 和 注释。有关更多详细信息,请参阅 annotation support 部分中的相应条目。@Commit@Rollbackspring-doc.cn

程序化事务管理

您可以使用静态 方法。例如,您可以在 test 中使用 methods、before methods和 after 方法来开始或结束当前的测试托管 transaction 或配置当前测试托管的事务以进行 rollback 或 commit。 只要启用,支持就会自动可用。TestTransactionTestTransactionTestTransactionTransactionalTestExecutionListenerspring-doc.cn

以下示例演示了 .请参阅 javadoc 获取更多详细信息。TestTransactionspring-doc.cn

@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"))
	}
}

在事务外部运行代码

有时,您可能需要在事务测试之前或之后运行某些代码 方法,但在事务上下文之外,例如,要验证初始 数据库状态 行为(如果测试配置为提交事务)。 支持此类方案的 AND 注释。您可以使用以下方法之一对测试类中的任何方法或测试接口中的任何默认方法进行注释 annotations 的 API 中,并确保 transaction method 或在 transaction method 在适当的时间运行之后。TransactionalTestExecutionListener@BeforeTransaction@AfterTransactionvoidvoidTransactionalTestExecutionListenerspring-doc.cn

任何 before 方法(例如用 JUnit Jupiter 注释的方法) 和任何 after 方法(例如用 JUnit Jupiter's 注释的方法)都是 run 在事务中。此外,对于未配置为在 交易。@BeforeEach@AfterEach@BeforeTransaction@AfterTransaction

配置事务管理器

TransactionalTestExecutionListener期望 bean 为 在 Spring 中为测试定义。如果有多个实例 of 中,您可以声明一个 限定符,或者可以由类实现。查阅 javadoc 对于 TestContextTransactionUtils.retrieveTransactionManager(),有关 算法用于在测试的 .PlatformTransactionManagerApplicationContextPlatformTransactionManagerApplicationContext@Transactional("myTxMgr")@Transactional(transactionManager = "myTxMgr")TransactionManagementConfigurer@ConfigurationApplicationContextspring-doc.cn

所有与事务相关的 Annotation 的演示

以下基于 JUnit Jupiter 的示例显示了一个虚构的集成测试 突出显示所有与事务相关的注释的方案。该示例并非有意为之 演示最佳实践,而是演示这些注释如何 使用。有关详细信息,请参阅注释支持部分 信息和配置示例。@Sql 的事务管理包含一个额外的示例,该示例用于 具有默认事务回滚语义的声明性 SQL 脚本执行。这 以下示例显示了相关的注释:@Sqlspring-doc.cn

@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 状态的应用程序代码时 persistence 上下文,请确保刷新测试方法中的底层工作单元 运行该代码。未能刷新基础工作单元可能会产生 false positives:您的测试通过,但相同的代码在实时生产中引发异常 环境。请注意,这适用于维护内存单元的任何 ORM 框架 的工作。在下面基于 Hibernate 的示例测试用例中,一种方法演示了 false positive,而另一种方法正确地公开了刷新 会期:spring-doc.cn

// ...

@Autowired
SessionFactory sessionFactory;

@Transactional
@Test // no expected exception!
public void falsePositive() {
	updateEntityInHibernateSession();
	// False positive: an exception will be thrown once the Hibernate
	// Session is finally flushed (i.e., in production code)
}

@Transactional
@Test(expected = ...)
public void updateWithSessionFlush() {
	updateEntityInHibernateSession();
	// Manual flush is required to avoid false positive in test
	sessionFactory.getCurrentSession().flush();
}

// ...
// ...

@Autowired
lateinit var sessionFactory: SessionFactory

@Transactional
@Test // no expected exception!
fun falsePositive() {
	updateEntityInHibernateSession()
	// False positive: an exception will be thrown once the Hibernate
	// Session is finally flushed (i.e., in production code)
}

@Transactional
@Test(expected = ...)
fun updateWithSessionFlush() {
	updateEntityInHibernateSession()
	// Manual flush is required to avoid false positive in test
	sessionFactory.getCurrentSession().flush()
}

// ...

以下示例显示了 JPA 的匹配方法:spring-doc.cn

// ...

@PersistenceContext
EntityManager entityManager;

@Transactional
@Test // no expected exception!
public void falsePositive() {
	updateEntityInJpaPersistenceContext();
	// False positive: an exception will be thrown once the JPA
	// EntityManager is finally flushed (i.e., in production code)
}

@Transactional
@Test(expected = ...)
public void updateWithEntityManagerFlush() {
	updateEntityInJpaPersistenceContext();
	// Manual flush is required to avoid false positive in test
	entityManager.flush();
}

// ...
// ...

@PersistenceContext
lateinit var entityManager:EntityManager

@Transactional
@Test // no expected exception!
fun falsePositive() {
	updateEntityInJpaPersistenceContext()
	// False positive: an exception will be thrown once the JPA
	// EntityManager is finally flushed (i.e., in production code)
}

@Transactional
@Test(expected = ...)
void updateWithEntityManagerFlush() {
	updateEntityInJpaPersistenceContext()
	// Manual flush is required to avoid false positive in test
	entityManager.flush()
}

// ...
测试 ORM 实体生命周期回调

与在测试 ORM 代码时避免误报的注释类似,如果您的应用程序使用实体生命周期回调(还有 称为实体侦听器),请确保刷新 test 中的底层工作单元 运行该代码的方法。未能刷新清除基础工作单元可能会 导致某些生命周期回调未被调用。spring-doc.cn

例如,当使用 JPA、 、 和 callback 时 不会调用,除非在实体被 saved 或 updated。同样,如果实体已附加到当前工作单元 (与当前持久化上下文相关联)时,尝试重新加载实体将 not 导致回调,除非在 尝试重新加载实体。@PostPersist@PreUpdate@PostUpdateentityManager.flush()@PostLoadentityManager.clear()spring-doc.cn

以下示例显示如何刷新 以确保在保留实体时调用回调。具有 已为 例。EntityManager@PostPersist@PostPersistPersonspring-doc.cn

// ...

@Autowired
JpaPersonRepository repo;

@PersistenceContext
EntityManager entityManager;

@Transactional
@Test
void savePerson() {
	// EntityManager#persist(...) results in @PrePersist but not @PostPersist
	repo.save(new Person("Jane"));

	// Manual flush is required for @PostPersist callback to be invoked
	entityManager.flush();

	// Test code that relies on the @PostPersist callback
	// having been invoked...
}

// ...
// ...

@Autowired
lateinit var repo: JpaPersonRepository

@PersistenceContext
lateinit var entityManager: EntityManager

@Transactional
@Test
fun savePerson() {
	// EntityManager#persist(...) results in @PrePersist but not @PostPersist
	repo.save(Person("Jane"))

	// Manual flush is required for @PostPersist callback to be invoked
	entityManager.flush()

	// Test code that relies on the @PostPersist callback
	// having been invoked...
}

// ...

有关使用所有 JPA 生命周期回调的工作示例,请参见 Spring Framework 测试套件中的JpaEntityListenerTestsspring-doc.cn