From cdc522242f9746b8c06b6bafda9e03ef5143e224 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sun, 31 Aug 2014 22:53:43 +0200 Subject: [PATCH] Document programmatic tx mgt support in the TCF This commit introduces a new "Programmatic transaction management" section in the Testing chapter of the reference manual with an example highlighting the new support provided via TestTransaction. In addition, this commit begins the work to be addressed more thoroughly in SPR-11399 by overhauling the entire "Transaction management" section with in-depth discussions on the following topics. - Test-managed transactions - Enabling and disabling transactions - Transaction rollback and commit behavior - Executing code outside of a transaction - Configuring a transaction manager Issue: SPR-11941, SPR-11399 --- src/asciidoc/index.adoc | 225 ++++++++++++++++++++++++++++++++-------- 1 file changed, 182 insertions(+), 43 deletions(-) diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc index e020bc7da22..91f6c4820fc 100644 --- a/src/asciidoc/index.adoc +++ b/src/asciidoc/index.adoc @@ -20901,36 +20901,158 @@ configured theme. } ---- - [[testcontext-tx]] ===== Transaction management + In the TestContext framework, transactions are managed by the -`TransactionalTestExecutionListener`. Note that `TransactionalTestExecutionListener` is -configured by default, even if you do not explicitly declare `@TestExecutionListeners` -on your test class. To enable support for transactions, however, you must provide a -`PlatformTransactionManager` bean in the application context loaded by -`@ContextConfiguration` semantics. In addition, you must declare `@Transactional` either -at the class or method level for your tests. - -For class-level transaction configuration (i.e., setting an explicit bean name for the -transaction manager and the default rollback flag), see the `@TransactionConfiguration` -entry in the <> section. - -If transactions are not enabled for the entire test class, you can annotate methods -explicitly with `@Transactional`. To control whether a transaction should commit for a -particular test method, you can use the `@Rollback` annotation to override the -class-level default rollback setting. - -__<> for +details). + +[[testcontext-tx-enabling-transactions]] +====== Enabling and disabling transactions + +Annotating a test method with `@Transactional` causes the test to be run within a +transaction that will, by default, be automatically rolled back after completion of the +test. If a test class is annotated with `@Transactional`, each test method within that +class hierarchy will be run within a transaction. Test methods that are not annotated +with `@Transactional` (at the class or method level) will not be run within a +transaction. Furthermore, tests that are annotated with `@Transactional` but have the +`propagation` type set to `NOT_SUPPORTED` will not be run within a transaction. + +__Note that <> and <> are preconfigured for transactional support at the class level.__ -Occasionally you need to execute certain code before or after a transactional test -method but outside the transactional context, for example, to verify the initial -database state prior to execution of your test or to verify expected transactional -commit behavior after test execution (if the test was configured not to roll back the -transaction). `TransactionalTestExecutionListener` supports the `@BeforeTransaction` and +The following example demonstrates a common scenario for writing an integration test for +a Hibernate-based `UserRepository`. As explained in +<>, there is no need to clean up the +database after the `createUser()` method is executed since any changes made to the +database will be automatically rolled back by the `TransactionalTestExecutionListener`. +See the <> section for an additional example. + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @RunWith(SpringJUnit4ClassRunner.class) + @ContextConfiguration(classes = TestConfig.class) + @Transactional + public class HibernateUserRepositoryTests { + + @Autowired + HibernateUserRepository repository; + + @Autowired + SessionFactory sessionFactory; + + JdbcTemplate jdbcTemplate; + + @Autowired + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Test + public 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); + } + + protected int countRowsInTable(String tableName) { + return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName); + } + + protected void assertNumUsers(int expected) { + assertEquals("Number of rows in the 'user' table.", expected, countRowsInTable("user")); + } + } +---- + +[[testcontext-tx-rollback-and-commit-behavior]] +====== Transaction rollback and commit behavior + +By default, test transactions will be automatically rolled back after completion of the +test; however, transactional commit and rollback behavior can be configured declaratively +via the class-level `@TransactionConfiguration` and method-level `@Rollback` annotations. +See the corresponding entries in the <> section for further details. + +[[testcontext-tx-programmatic-tx-mgt]] +====== Programmatic transaction management +As of Spring Framework 4.1, it is possible to interact with test-managed transactions +_programmatically_ via the static methods in `TestTransaction`. For example, +`TestTransaction` may be used within _test_ methods, _before_ methods, and _after_ +methods to start or end the current test-managed transaction or to configure the current +test-managed transaction for rollback or commit. Support for `TestTransaction` is +automatically available whenever the `TransactionalTestExecutionListener` is enabled. + +The following example demonstrates some of the features of `TestTransaction`. Consult the +javadocs for `TestTransaction` for further details. + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @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")); + } + } +---- + +[[testcontext-tx-before-and-after-tx]] +====== Executing code outside of a transaction + +Occasionally you need to execute certain code before or after a transactional test method +but outside the transactional context -- for example, to verify the initial database state +prior to execution of your test or to verify expected transactional commit behavior after +test execution (if the test was configured not to roll back the transaction). +`TransactionalTestExecutionListener` supports the `@BeforeTransaction` and `@AfterTransaction` annotations exactly for such scenarios. Simply annotate any `public void` method in your test class with one of these annotations, and the `TransactionalTestExecutionListener` ensures that your __before transaction method__ or @@ -20938,17 +21060,35 @@ __after transaction method__ is executed at the appropriate time. [TIP] ==== -Any __before methods__ (such as methods annotated with JUnit's `@Before`) and any -__after methods__ (such as methods annotated with JUnit's `@After`) are executed -__within__ a transaction. In addition, methods annotated with `@BeforeTransaction` or +Any __before methods__ (such as methods annotated with JUnit's `@Before`) and any __after +methods__ (such as methods annotated with JUnit's `@After`) are executed __within__ a +transaction. In addition, methods annotated with `@BeforeTransaction` or `@AfterTransaction` are naturally not executed for test methods that are not configured to run within a transaction. ==== +[[testcontext-tx-mgr-config]] +====== Configuring a transaction manager + +`TransactionalTestExecutionListener` expects a `PlatformTransactionManager` bean to be +defined in the Spring `ApplicationContext` for the test. In case there are multiple +instances of `PlatformTransactionManager` within the test's `ApplicationContext`, +`@TransactionConfiguration` supports configuring the bean name of the +`PlatformTransactionManager` that should be used to drive transactions. Alternatively, a +_qualifier_ may be declared via `@Transactional("myQualifier")`, or +`TransactionManagementConfigurer` can be implemented by an `@Configuration` class. +Consult the javadocs for `TestContextTransactionUtils.retrieveTransactionManager()` for +details on the algorithm used to look up a transaction manager in the test's +`ApplicationContext`. + +[[testcontext-tx-annotation-demo]] +====== Demonstration of all transaction-related annotations + The following JUnit-based example displays a fictitious integration testing scenario -highlighting several transaction-related annotations. Consult the -<> section for further information -and configuration examples. <> section for further +information and configuration examples. <> contains an additional example using `@Sql` for declarative SQL script execution with default transaction rollback semantics. @@ -20991,7 +21131,6 @@ declarative SQL script execution with default transaction rollback semantics. } ---- - [[testcontext-tx-false-positives]] .Avoid false positives when testing ORM code [NOTE] @@ -21892,11 +22031,11 @@ tests] of client-side REST tests. [[testing-examples-petclinic]] ==== PetClinic Example + The PetClinic application, available on -https://github.com/spring-projects/spring-petclinic[Github], illustrates several features -of the __Spring TestContext Framework__ in a JUnit environment. Most test -functionality is included in the `AbstractClinicTests`, for which a partial listing -is shown below: +https://github.com/spring-projects/spring-petclinic[GitHub], illustrates several features +of the __Spring TestContext Framework__ in a JUnit environment. Most test functionality +is included in the `AbstractClinicTests`, for which a partial listing is shown below: [source,java,indent=0] [subs="verbatim,quotes"] @@ -21934,16 +22073,16 @@ Notes: `TransactionalTestExecutionListener`). * The `clinic` instance variable -- the application object being tested -- is set by Dependency Injection through `@Autowired` semantics. -* The `getVets()` method illustrates how you can use the inherited - `countRowsInTable()` method to easily verify the number of rows in a given table, thus - verifying correct behavior of the application code being tested. This allows for - stronger tests and lessens dependency on the exact test data. For example, you can add - additional rows in the database without breaking tests. +* The `getVets()` method illustrates how you can use the inherited `countRowsInTable()` + method to easily verify the number of rows in a given table, thus verifying correct + behavior of the application code being tested. This allows for stronger tests and + lessens dependency on the exact test data. For example, you can add additional rows in + the database without breaking tests. * Like many integration tests that use a database, most of the tests in - `AbstractClinicTests` depend on a minimum amount of data already in the database - before the test cases run. Alternatively, you might choose to populate the database - within the test fixture set up of your test cases -- again, within the same - transaction as the tests. + `AbstractClinicTests` depend on a minimum amount of data already in the database before + the test cases run. Alternatively, you might choose to populate the database within the + test fixture set up of your test cases -- again, within the same transaction as the + tests. The PetClinic application supports three data access technologies: JDBC, Hibernate, and JPA. By declaring `@ContextConfiguration` without any specific resource locations, the