简体   繁体   中英

@Before and @Transactional

I have

@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(defaultRollback = true, transactionManager = "transactionManager")

   @Before
   @Transactional
   public void mySetup() {
      // insert some records in db
   }

   @After
   @Transactional
   public void myTeardown() {
      // delete some records
   }

   @Test
   @Transactional
   public void testMy() {
      // do stuff
   }

My question is: will mySetup, testMy and myTeardown all run within the same transaction? It seems like they should, but I'm getting some strange error which might suggest that they are stepping on each other.

Yes, all three methods will run within the same transaction. See section TestContext Framework/Transaction management in the reference docs:

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

Thus the @Transactional annotation on mySetup() and myTeardown() is kind of redundant, or might be even considered misleading, as their transactionality is determined by the individual test method being currently executed.

This is because the beforeTestMethod() and afterTestMethod() callbacks of TransactionalTestExecutionListener (responsible for starting/completing the transaction) are executed before JUnit's @Before and after JUnit's @After methods, respectively.

Some changes occurred in Spring 5. According to the documentation :

Method-level lifecycle methods — for example, methods annotated with JUnit Jupiter's @BeforeEach or @AfterEach — are run within a test-managed transaction.

On the other hand, suite-level and class-level lifecycle methods — for example, methods annotated with JUnit Jupiter's @BeforeAll or @AfterAll and methods annotated with TestNG's @BeforeSuite , @AfterSuite , @BeforeClass , or @AfterClassare not run within a test-managed transaction .

If you need to execute code in a suite-level or class-level lifecycle method within a transaction, you may wish to inject a corresponding PlatformTransactionManage r into your test class and then use that with a TransactionTemplate for programmatic transaction managemen t.

If you annotate your @Before and @After methods with @Transactional, they will not be run in a transaction. But if you use @Transactional for your test methods (methods that have @Test on them) or your test class as a whole, every test method will be run in different transaction and @Before and @After methods will also be run in the same transaction as for each @Test method. For more illustration, see these two code snippets:

@Transactional
public class MyTestClass {
    @Before
    public void beforeTest() {
        ...
    }

    @Test
    void testMethod1() {
        ...
    }

    @Test
    void testMethod2() {
        ...
    }

    @After
    public void afterTest() {
        ...
    }
}

The above code runs in exactly the same way as the below code:

public class MyTestClass {
    @Before
    public void beforeTest() {
        ...
    }

    @Test
    @Transactional
    void testMethod1() {
        ...
    }

    @Test
    @Transactional
    void testMethod2() {
        ...
    }

    @After
    public void afterTest() {
        ...
    }
}

In these two code snippets for each "testMethod1" and "testMethod2" methods there would be a different transaction. And also both "beforeMethod" and "afterMethod" methods would be run in each test method's transaction respectively.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM