Skip to content

JUnit @Rule executes outside of transaction when using the TransactionalTestExecutionListener [SPR-9232] #13870

@spring-projects-issues

Description

@spring-projects-issues

Phil Webb opened SPR-9232 and commented

Status Quo

The order in which JUnit rules are run was changed between Spring 3.0 and 3.1 to match JUnit (see #12361). As a side effect of this, @Rule callbacks (such as the one developed for #11259) are now executed after the callbacks in TestExecutionListeners. This can be problematic if your rule is running within a transaction as the TransactionalTestExecutionListener will perform the rollback before the rule runs. The opposite may also be true: a transaction might not be started before the rule runs.

Current Implementation of SpringJUnit4ClassRunner.methodBlock()
Statement statement = methodInvoker(frameworkMethod, testInstance);
statement = possiblyExpectingExceptions(frameworkMethod, testInstance, statement);
statement = withBefores(frameworkMethod, testInstance, statement);
statement = withAfters(frameworkMethod, testInstance, statement);
statement = withRulesReflectively(frameworkMethod, testInstance, statement);
statement = withPotentialRepeat(frameworkMethod, testInstance, statement);
statement = withPotentialTimeout(frameworkMethod, testInstance, statement);

Goals

Ideally, one could argue that in most circumstances the TransactionalTestExecutionListener should always be the first and last thing to run.

Deliverables

  1. Consider changing the SpringJUnit4ClassRunner.methodBlock() method to call TestExecutionListener callbacks outside of JUnit @Before calls, something like:
Statement statement = methodInvoker(frameworkMethod, testInstance);
statement = possiblyExpectingExceptions(frameworkMethod, testInstance, statement);
statement = withTestExecutionListenerBefores(frameworkMethod, testInstance, statement);
statement = withTestExecutionListenerAfters(frameworkMethod, testInstance, statement);
statement = withRulesReflectively(frameworkMethod, testInstance, statement);
statement = withBefores(frameworkMethod, testInstance, statement);
statement = withAfters(frameworkMethod, testInstance, statement);
statement = withPotentialRepeat(frameworkMethod, testInstance, statement);
statement = withPotentialTimeout(frameworkMethod, testInstance, statement);

This would ensure that the before and after callbacks of TransactionalTestExecutionListener (and any TestExecutionListener) get called around the @Rule.

Alternatives

Another option would be to introduce a RuleAwareTestExecutionListener interface that adds beforeRules() and afterRules() methods and have TransactionalTestExecutionListener implement this interface as well (or possibly instead of) TestExecutionListener.


Affects: 3.1.1

Issue Links:

4 votes, 6 watchers

Metadata

Metadata

Assignees

Labels

in: testIssues in the test modulestatus: declinedA suggestion or change that we don't feel we should currently applytype: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions