Spring Cache and Integration Testing
Learn more about using Spring Cache for integration testing that can greatly improve your app's performance.
Join the DZone community and get the full member experience.
Join For FreeUsing caches in a Spring application is relatively easy. It makes the overall performance much better, but it also opens up some exciting problems when it comes to integration testing within the application. Adding caching behavior to a functionality without properly thinking over the consequences is one of the biggest mistakes that a beginner programmer can make. You must have understanding and control over how your application works with the selected cache implementation, and you must be able to test it.
Also, your unit test cases must be namely independent from each other and should not depend on the order of the methods being executed.
Unfortunately, when you execute the integration test with @SpringRunner
, the Spring environment will be started once. And therefore, the caches might contain data from previous test executions in case of subsequent calls.
In the common Spring integration test scenario, the test method or class uses some predefined data from a memory database. While Repository classes are the perfect candidate for caching, data from previous test execution might remain in the cache even if you carefully drop and recreate all tables in your memory database.
Localizing such problems can be really complicated while your method or even your class runs without a problem when you start it without other test cases.
In order for make your application eligible for integration testing, you have the following possibilities. You can choose the one that fits to your integration test requirements.
Use Spring Boot Dev Tools
I generally recommend using Spring Boot Dev Tools. It makes the whole development process way faster. It also turns caching off automatically during the development phase.
Unfortunately, it is not so easy to enable and disable Dev Tools according to your needs for given test classes and to test your application with and without caching. It requires you to overwrite caching, setting your configuration file for the corresponding profile where you want to enable caching. You also need to use multiple integration test profiles, which is bad for the general maintainability.
Turning Off Cache for Integration Test Profile
It is possible to turn off caching in the configuration file for a given profile using:
spring.cache.type=NONE
Another solution with the same result is to use a mock implementation of Spring caching for the integration test profile. Spring already has such an implementation out of the box called NoOpCacheManager. You need to define the cache manager in the configuration, like this:
/** * Disabling cache for integration test */
@Bean public CacheManager cacheManager() {
return new NoOpCacheManager();
}
Both methods have the same disadvantage as using Dev Tools. Either caching cannot be tested at all during the integration test, or you need to define a multiple profiles for integration tests with and without caching.
Evict Caches Manually
In order to get full control over testing your application with caching, I recommend using a single integration test profile, with caching turned on, and handling the cache eviction manually. It makes your test structure simpler and allows you to test non-obvious cases as well.
- What if your call gets the result from cache?
- What if the cache is empty?
- What if the cache is full with other entities and your result gets removed from the cache?
- Does cache eviction work by data insert, update, or delete as you expected?
- Have you defined to cache null values as well?
- Did you define your key for caching correctly?
All the above scenarios can be covered by playing with the CacheManager and Cache interfaces.
To get a clean environment before each test execution, you should evict all caches before running an integration test function. The following method iterates through all Spring caches and evicts them.
@Autowired private CacheManager cacheManager;
public void evictAllCaches(){
for(String name : cacheManager.getCacheNames()){
cacheManager.getCache(name).clear();
}
}
You need to call this method every time you test a function that uses caching. You can do it manually as the first command in your test method, or obviously, you can also do it in the test initialization phase, also known as the method annotated with@Before
.
The risk invovled with this solution is that you will forget it. Therefore, I find it a good habit to implement an abstract parent class for all cache related tests, define the evictAllCaches
method in it, and annotate with @Before
. This way, you also have the ability to find all cache-related classes fast, using the class hierarchy. You can also see which classes should be able to work independent from caching, which is also a very important aspect.
Published at DZone with permission of Peter Varga. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments