我开发了一个Spring MemberRepository
接口,扩展了org.springframework.data.jpa.repository.JpaRepository
。 MemberRepository
有一个方法:
@Cacheable(CacheConfiguration.DATABASE_CACHE_NAME)
Member findByEmail(String email);
结果由Spring缓存抽象缓存(由ConcurrentMapCache
支持)。
我遇到的问题是我想编写一个集成测试(针对hsqldb),该测试断言第二次从
我最初想过嘲笑jpa基础架构(实体管理器等),并以某种方式断言第二次不调用实体管理器,但它似乎太难/繁琐(参见https://stackoverflow.com/a/23442457/536299)。
那么有人可以就如何测试使用@Cacheable
注释的Spring Data Repository方法的缓存行为提供建议吗?
答案 0 :(得分:52)
如果要测试缓存等技术方面,请不要使用数据库。了解您要在此测试的内容非常重要。您希望确保使用相同的参数调用方法调用。面向数据库的存储库与该主题完全正交。
以下是我的建议:
@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));
}
}
正如您所看到的,我们在这里做了一些过度测试:
答案 1 :(得分:1)
我尝试使用Oliver的示例在我的应用中测试缓存行为。在我的情况下,我的缓存设置在服务层,我想验证我的repo被调用正确的次数。我正在使用spock mocks而不是mockito。我花了一些时间试图找出我的测试失败的原因,直到我意识到首先运行的测试填充缓存并影响其他测试。清除每个测试的缓存后,他们开始按预期运行。
这是我最终的结果:
@ContextConfiguration
class FooBarServiceCacheTest extends Specification {
@TestConfiguration
@EnableCaching
static class Config {
def mockFactory = new DetachedMockFactory()
def fooBarRepository = mockFactory.Mock(FooBarRepository)
@Bean
CacheManager cacheManager() {
new ConcurrentMapCacheManager(FOOBARS)
}
@Bean
FooBarRepository fooBarRepository() {
fooBarRepository
}
@Bean
FooBarService getFooBarService() {
new FooBarService(fooBarRepository)
}
}
@Autowired
@Subject
FooBarService fooBarService
@Autowired
FooBarRepository fooBarRepository
@Autowired
CacheManager cacheManager
def "setup"(){
// we want to start each test with an new cache
cacheManager.getCache(FOOBARS).clear()
}
def "should return cached foobars "() {
given:
final foobars = [new FooBar(), new FooBar()]
when:
fooBarService.getFooBars()
fooBarService.getFooBars()
final fooBars = fooBarService.getFooBars()
then:
1 * fooBarRepository.findAll() >> foobars
}
def "should return new foobars after clearing cache"() {
given:
final foobars = [new FooBar(), new FooBar()]
when:
fooBarService.getFooBars()
fooBarService.clearCache()
final fooBars = fooBarService.getFooBars()
then:
2 * fooBarRepository.findAll() >> foobars
}
}