Spring integration testing III: manual cleaning of all storage types
- Cyril Sahula
- Apr 13, 2023
- 3 min read
Updated: Dec 14, 2023

This article belongs to a series about integration testing. I recommend reading the kick-offand the second part about Spring default behavior articles beforehand.
I explained in the previous article that the default approach in Spring Testing Framework is not the best for most advanced cases because of its limitations which are: It can clean just transaction DBs and works only in the simple application architecture. In this article, I would like to describe another way that has, of course, its pros and cons but it fits more project designs. The biggest deal in testing is to achieve idempotency. Practically, it means that test cases should not propagate their state to the following ones or such a state must be cleanedbetween test cases. If we consider an ordinary Spring application, the state can be propagated to either application context or to a datastore of any type (SQL DB, NoSQL DB, cache, message broker, etc.). How to clean the application context properly I will write in the next articles. This approach is focused on the problem of how to clean data between test cases and overcome the limits of the automatic rollback model from the first article.

Cleaning without a transaction context
This code example does not have any transaction context in the test class. Applications either do not use transactional DB at all or do not always transaction context needed in a test class even when an application uses transactional DB. In such cases, I recommend turning testing transactions off or do not enable them in test classes. And thus, data cleaningmust be solved between test cases in a code. Cleaning itself can be implemented in many ways, in the project I demonstrate just the principle. As I described in the picture above a callof a cleaning method must be invoked before each test case (removes even data after unsuccessful runs). Of course, each test case must either prepare its own data for testing or call a generic method that creates some basic data.
Cleaning with a transaction context
This code example has a common Spring transaction context in the test class. When the @Transactional annotation is placed into a test class, all problems which I described in the first article will appear. The transaction context behaves as if it has ISOLATION_SERIALIZABLE isolation but documentation describes that it does not support transaction isolation at all. It just behaves like it. Practically said: the test class is not able to see data from other sessions. To deal with the problem and still have an ordinary Spring transaction context, the TransactionalTestExecutionListener must be removed. Without the listener, data is committed into DB, however, the cleaning mechanism will take care of it. Thanks to committing data, we also avoid a false positive issue like this. Unfortunately, annotation @TestExecutionListeners does not provide a remove method and thus all listeners must be listed. During upgrading the Spring dependencies list should be checked. The configuration is here.
Conclusion
The article presents a model which fits a lot of application architectures and is not dependent on a data storage type. The biggest disadvantage is that test cases cannot be run against DB with data that must not be deleted. We must not perform such tests against real environments (testing, preproduction, production). The test run would clean data from databases.
Pros
can clean all storage types
tests real DB transactions which start at the same place as in the production solution
Cons
test cases can not run parallel
cannot be run against databases where data must not be deleted (but we usually donot run such tests against reals DBs)
I use this configuration for most of the projects because it is simple and can clean all storagetypes.
Comments