Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-9232

JUnit @Rule executes outside of transaction when using the TransactionalTestExecutionListener

    Details

    • Type: Improvement
    • Status: Resolved
    • Priority: Minor
    • Resolution: Works as Designed
    • Affects Version/s: 3.1.1
    • Fix Version/s: None
    • Component/s: Test
    • Labels:
      None
    • Last commented by a User:
      false

      Description

      Status Quo

      The order in which JUnit rules are run was changed between Spring 3.0 and 3.1 to match JUnit (see SPR-7705). As a side effect of this, @Rule callbacks (such as the one developed for SPR-6593) 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.

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                sbrannen Sam Brannen
                Reporter:
                pwebb Phil Webb
                Last updater:
                Sam Brannen
              • Votes:
                4 Vote for this issue
                Watchers:
                6 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:
                  Days since last comment:
                  3 years, 1 week, 4 days ago