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
- 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
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,
@Rulecallbacks (such as the one developed for #11259) are now executed after the callbacks inTestExecutionListeners. This can be problematic if your rule is running within a transaction as theTransactionalTestExecutionListenerwill 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()Goals
Ideally, one could argue that in most circumstances the
TransactionalTestExecutionListenershould always be the first and last thing to run.Deliverables
SpringJUnit4ClassRunner.methodBlock()method to callTestExecutionListenercallbacks outside of JUnit@Beforecalls, something like:This would ensure that the before and after callbacks of
TransactionalTestExecutionListener(and anyTestExecutionListener) get called around the@Rule.Alternatives
Another option would be to introduce a
RuleAwareTestExecutionListenerinterface that addsbeforeRules()andafterRules()methods and haveTransactionalTestExecutionListenerimplement this interface as well (or possibly instead of)TestExecutionListener.Affects: 3.1.1
Issue Links:
@Rulealternative to SpringJUnit4ClassRunner ("depends on")@Ruleexecution order4 votes, 6 watchers