Spring Testing is a good tool for testing application written using Spring framework. It has convenient built-in transaction management for integration tests. By default, Spring starts a transaction for each testing method and rollback the transaction after testing method is executed. Methods annotated with @Before and @After are also executed in the same transaction. By doing this, no change is actually made to the database, so you don't need to clean the database manually after each test.

Although this automatic transaction management is considered harmful for some cases, it's very handy for most cases. I did encounter some cases when you had to find workrounds.

In one test case, some database setup is required for all testing methods. So a method with @Before is created with necessary code to do that. In the acutal testing method, a background service is triggered to run some tasks and then the result is verified. In this case, the background service is running in a separate thread and reads data created in @Before method from database. But because @Before and current testing method is running in the same transaction, before the testing method finishes and the transaction is committed, the data changes are not written to database. So the background service cannot see the data and always fails.

Programmatic transaction management

To workaround this, I changed the @Before to @BeforeTransaction, which makes the method executed before the transaction starts. Then use programmatic transaction management to commit the transaction.

@Autowired
protected PlatformTransactionManager transactionManager;

@BeforeTransaction
public void setup() {  
  DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
  TransactionStatus transaction = transactionManager.getTransaction(definition);

  //database setup

  transactionManager.commit(transaction);
}

As shown in code above, data created in setup method is written to database and visible to following testing methods.

JUnit execution order

Another solution is to leverage test execution order introduced in JUnit 4.11. The idea is to make the @Before method as a testing method, but is executed before other testing methods.

@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MyTest {

  @Test
  @Transactional
  public void testMethod0() {
    //database setup
  }

  @Test
  @Transactional
  public void testMethod1() {
    //actual testing code
  }
}

As shown in the code above, defaultRollback of @TransactionConfiguration should be set to false, then database changes won't be rolled back. Use @FixMethodOrder(MethodSorters.NAME_ASCENDING) to tell JUnit to execute methods in ascending order of method names. testMethod0 is the method for database setup and is executed before the actual testing method testMethod1.

No automatic transaction management

If automatic transaction management introduces more trouble than it solves, you can just disable the automatic transaction management.

To disable automatic transaction management for a test class, use @TestExecutionListeners and exclude TransactionalTestExecutionListener.class from listeners. By default, TransactionalTestExecutionListener is included.

@TestExecutionListeners(listeners = {DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})
public class MyTest {

}

For individual test, use @Transactional(propagation = Propagation.NOT_SUPPORTED) to exclude single testing method from transaction.

Note

This actually shows a very common case in daily development. 95% of time, good framework , like Spring, can help you a lot. But the rest 5% of time, you'll need to find the answer youself. In this case, unfortunately 95% of online resources cannot help you. You have to dig into the reference guide and source code to find out the answer.