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 aPlatformTransactionManager
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 eitherRANDOM_PORT
orDEFINED_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
What Java 8 Stream.Collect Equivalents Are Available in the Standard Kotlin Library
No Exception While Type Casting with a Null in Java
Difference Between "On-Heap" and "Off-Heap"
How Is the Fork/Join Framework Better Than a Thread Pool
Why We Shouldn't Make a Spring MVC Controller @Transactional
Why Is Maven Downloading the Maven-Metadata.Xml Every Time
Junit Confusion: Use 'Extends Testcase' or '@Test'
What Is the Default Max Heap Size (-Xmx) in Java 8
Writing a Large Resultset to an Excel File Using Poi
Why Do We Need Interfaces in Java
How to Iterate Through Range of Dates in Java
Random "Element Is No Longer Attached to the Dom" Staleelementreferenceexception
Calling Jmx Mbean Method from a Shell Script
Difference Between @Onetomany and @Elementcollection
Hibernate: Flush() and Commit()