Search code examples
javaspring-boottestingcucumberh2

Rollback transactions after each Cucumber scenarios with Spring Boot


I'm using the BDD testing framework Cucumber with Spring Boot 2.5. I want to rollback transactions or reset my database after each Cucumber scenarios. I'm using an H2 database, populate by dynamically generated data. I tried @Transactional, but it doesn't work and @DirtiesContext is to slow.


Solution

  • 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