Rollback Transaction After @Test

Rollback transaction after @Test

Just add @Transactional annotation on top of your test:

@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = {"testContext.xml"})
@Transactional
public class StudentSystemTest {

By default Spring will start a new transaction surrounding your test method and @Before/@After callbacks, rolling back at the end. It works by default, it's enough to have some transaction manager in the context.

From: 10.3.5.4 Transaction management (bold mine):

In the TestContext framework, transactions are managed by the TransactionalTestExecutionListener. Note that TransactionalTestExecutionListener is configured by default, even if you do not explicitly declare @TestExecutionListeners on your test class. To enable support for transactions, however, you must provide a PlatformTransactionManager bean in the application context loaded by @ContextConfiguration semantics. In addition, you must declare @Transactional either at the class or method level for your tests.

Transactions in spring boot testing not rolled back

According to the official Spring Boot documentation db transaction rollback is not supported when you apply it directly from the "web layer":

If your test is @Transactional, it will rollback the transaction at
the end of each test method by default. However, as using this
arrangement with either RANDOM_PORT or DEFINED_PORT implicitly
provides a real servlet environment, HTTP client and server will run
in separate threads, thus separate transactions. Any transaction
initiated on the server won’t rollback in this case.

I propose you to consider the following options:

  • Use separate tests for web controller layer and database layer in case of Unit testing

  • Create/Restore tables before & Drop/Clear them after the test method execution when integration tests are performed. This approach might have significant overhead when the Db schema is large, but you can clear/restore data selectively according to you demands.

Rollback transactions after each Cucumber scenarios with Spring Boot

The way to do this is to use the PlatformTransactionManager to start a transaction before each scenario and to roll it back after. This is essentially what TransactionalTestExecutionListener does when a JUnit test class annotated with @Transactional is executed.

In Cucumber you would do this using @Before and @After hooks. And because you may not want do this for every scenario you can choose to make the hooks conditional so that they only execute when a scenario is tagged in the right way.

For example:

@txn
Feature: Search

Background:
Given there is a user

Scenario: Find messages by content
Given a User has posted the following messages:
| content |
| I am making dinner |
| I just woke up |
| I am going to work |
When I search for "I am"
Then the results content should be:
| I am making dinner |
| I am going to work |
public class SpringTransactionHooks implements BeanFactoryAware {

private BeanFactory beanFactory;
private TransactionStatus transactionStatus;

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}

@Before(value = "@txn", order = 100)
public void startTransaction() {
transactionStatus = beanFactory.getBean(PlatformTransactionManager.class)
.getTransaction(new DefaultTransactionDefinition());
}

@After(value = "@txn", order = 100)
public void rollBackTransaction() {
beanFactory.getBean(PlatformTransactionManager.class)
.rollback(transactionStatus);
}

}

From:

https://github.com/cucumber/cucumber-jvm/tree/main/examples/spring-java-junit5

How to rollback a database transaction when testing services with Spring in JUnit?

You need to extend transaction boundaries to the boundaries of your test method. You can do it by annotating your test method (or the whole test class) as @Transactional:

@Test 
@Transactional
public void testInsert(){
long id=myService.addPerson("JUNIT");
assertNotNull(id);
if(id<1){
fail();
}
}

You can also use this approach to ensure that data was correctly written before rollback:

@Autowired SessionFactory sf;

@Test
@Transactional
public void testInsert(){
myService.addPerson("JUNIT");
sf.getCurrentSession().flush();
sf.getCurrentSession().doWork( ... check database state ... );
}


Related Topics



Leave a reply



Submit