I have developed a Spring Data repository, MemberRepository
interface, that extends org.springframework.data.jpa.repository.JpaRepository
. MemberRepository
has a method:
@Cacheable(CacheConfiguration.DATABASE_CACHE_NAME) Member findByEmail(String email);
The result is cached by Spring cache abstraction (backed by a ConcurrentMapCache
).
The issue I have is that I want to write an integration test (against hsqldb) that asserts that the result is retrieved from db the first time and from cache the second time.
I initially thought of mocking the jpa infrastructure (entity manager, etc.) and somehow assert that the entity manager is not called the second time but it seems too hard/cumbersome (see https://stackoverflow.com/a/23442457/536299).
Can someone then please provide advice as to how to test the caching behavior of a Spring Data Repository method annotated with @Cacheable
?
The Spring framework provides a simple way to cache the value returned by a method. Basically, you annotate a method with @Cacheable and next time you call it with the same arguments, you get the cached value avoiding re-executing the method's code.
If you want to test a technical aspect like caching, don't use a database at all. It's important to understand what you'd like to test here. You want to make sure the method invocation is avoided for the invocation with the very same arguments. The repository fronting a database is a completely orthogonal aspect to this topic.
Here's what I'd recommend:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class CachingIntegrationTest { // Your repository interface interface MyRepo extends Repository<Object, Long> { @Cacheable("sample") Object findByEmail(String email); } @Configuration @EnableCaching static class Config { // Simulating your caching configuration @Bean CacheManager cacheManager() { return new ConcurrentMapCacheManager("sample"); } // A repository mock instead of the real proxy @Bean MyRepo myRepo() { return Mockito.mock(MyRepo.class); } } @Autowired CacheManager manager; @Autowired MyRepo repo; @Test public void methodInvocationShouldBeCached() { Object first = new Object(); Object second = new Object(); // Set up the mock to return *different* objects for the first and second call Mockito.when(repo.findByEmail(Mockito.any(String.class))).thenReturn(first, second); // First invocation returns object returned by the method Object result = repo.findByEmail("foo"); assertThat(result, is(first)); // Second invocation should return cached value, *not* second (as set up above) result = repo.findByEmail("foo"); assertThat(result, is(first)); // Verify repository method was invoked once Mockito.verify(repo, Mockito.times(1)).findByEmail("foo"); assertThat(manager.getCache("sample").get("foo"), is(notNullValue())); // Third invocation with different key is triggers the second invocation of the repo method result = repo.findByEmail("bar"); assertThat(result, is(second)); } }
As you can see, we do a bit of over-testing here:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With