Description
Den Orlov opened SPR-5079 and commented
Background
I have integration tests that:
- put some test data into database using Hibernate
- wait some time, so DB stored procedures triggered by scheduler will process my data
- retrieve from db some data and check its correctness, again using Hibernate
When I used Spring's "JUnit 3.8 legacy support" all of these three steps were organized in one test method using the protected endTransaction()
and startNewTransaction()
methods in AbstractTransactionalSpringContextTests
between steps #1 and #2.
Status Quo
Now I am migrating my code to use the Spring TestContext Framework (TCF), but it doesn't provide support for programmatically starting or stopping the test-managed transaction.
Deliverables
The term test-manged means a transaction managed by the TCF, either declaratively or programmatically.
See also "Design Considerations" below.
- Introduce a mechanism in the TCF that supports the following features:
- end the current test-managed transaction
- start a new test-managed transaction
- configure the current test-managed transaction to be committed once the test completes
- configure the current test-managed transaction to be rolled back once the test completes
- Refactor
TransactionalTestExecutionListener
to use this new mechanism internally. - Consider introducing a JUnit
TestRule
that simplifies the programming model (e.g., by delegating to the proposedTestTransaction
façade).
Design Considerations
Introduce a TestTransaction
class that acts as a façade to the functionality previously available in TransactionalTestExecutionListener
. For example, TestTransaction
could define methods that could provide the following API:
TestTransaction.start()
TestTransaction.end()
TestTransaction.rollback(boolean)
Options for Interacting with the Test Transaction
Dependency Injection of TestTransaction
Ideally, we would like to be able to have the TestTransaction
injected into our test instance. However, since the DependencyInjectionTestExecutionListener
must come before the TransactionalTestExecutionListener
in the chain of listeners, dependency injection would only be possible by introducing yet another "transactional" TestExecutionListener
that creates a proxy bean or bean of type ObjectFactory
in the ApplicationContext
. Such a proxy bean would serve as a placeholder for dependency injection into the test instance, and the TransactionalTestExecutionListener
could later set a value either directly in the proxy/ObjectFactory
or via a ThreadLocal
.
But we would like to avoid having two (2) TestExecutionListener
implementations for transactional support. Plus, the placeholder-bean approach could present more problems than it solves.
Dependency Injection of TestContext
A second option would be to inject the TestContext
into test instances (see #12947) and provide access to the TestTransaction
as an attribute (note that TestContext
implements AttributeAccessor
), but this would open up use of the TestContext
within test classes (i.e., no longer limited to the TestExecutionListener
API). In addition, developers would be forced to navigate the TestContext
to obtain the TestTransaction
, thus making the programming model less intuitive.
Purely ThreadLocal Approach
The final option is to follow a purely ThreadLocal
-based approach with TestTransaction
encapsulating the details and providing static methods instead of instance methods.
Related Resources
- Diff for TransactionalTestExecutionListener. This commit introduced caching of the
TransactionContext
keyed byMethod
and removed the previous support for thetransactionFlaggedForCommit
functionality.
Affects: 2.5.6
Issue Links:
- Document programmatic test transaction management support [SPR-11941] #16558 Document programmatic test transaction management support ("is depended on by")
- Provide support in TestContext framework equivalent to AbstractTransactionalSpringContextTests [SPR-5879] #10548 Provide support in TestContext framework equivalent to AbstractTransactionalSpringContextTests ("is duplicated by")
- TransactionalTestExecutionListener uses cached TransactionManager (TM), even after ApplicationContext that created the TM has been marked 'dirty' and closed. [SPR-4638] #9315 TransactionalTestExecutionListener uses cached TransactionManager (TM), even after ApplicationContext that created the TM has been marked 'dirty' and closed.
- Introduce autowiring support for the current TestContext [SPR-8299] #12947 Introduce autowiring support for the current TestContext
- Transactional JUnit test with timeout does not create a transaction [SPR-4541] #9218 Transactional JUnit test with timeout does not create a transaction
Referenced from: commits bdceaa4, f667e43, 90f0d14
30 votes, 22 watchers