Spring Data 저장소에서 Spring의 선언 캐싱 지원을 테스트하는 방법은 무엇입니까?
Spring Data 저장소를 개발했습니다.MemberRepository
인터페이스, 확장되는org.springframework.data.jpa.repository.JpaRepository
.MemberRepository
메소드가 있습니다.
@Cacheable(CacheConfiguration.DATABASE_CACHE_NAME)
Member findByEmail(String email);
결과는 Spring 캐시 추상화(에 의해 지원됨)에 의해 캐시됩니다.ConcurrentMapCache
).
제가 가진 문제는 (hsqldb에 대한) 통합 테스트를 작성하고 싶은 것입니다. 결과를 db에서 첫 번째로 검색하고 캐시에서 두 번째로 검색한다는 것을 주장하는 것입니다.
저는 처음에 jpa 인프라(인테리어 매니저 등)를 조롱할 생각을 했는데 어떻게든 엔티티 매니저가 두 번째로 불리는 것이 아니라 너무 어렵고 번거로운 것 같다고 주장합니다(https://stackoverflow.com/a/23442457/536299) 참조).
그러면 다음과 같이 주석이 달린 Spring Data Repository 메서드의 캐싱 동작을 테스트하는 방법에 대한 조언을 제공해 주실 수 있습니까?@Cacheable
?
캐싱과 같은 기술적 측면을 테스트하려면 데이터베이스를 전혀 사용하지 마십시오.여기서 테스트하려는 내용을 이해하는 것이 중요합니다.동일한 인수를 사용하는 호출에 대해 메서드 호출을 방지하려고 합니다.데이터베이스 전면의 리포지토리는 이 항목과 완전히 직교하는 측면입니다.
제가 추천하고 싶은 것은 다음과 같습니다.
- 선언 캐싱을 구성하는 통합 테스트를 설정하거나 프로덕션 구성에서 필요한 비트와 조각을 가져옵니다.
- 리포지토리의 모의 인스턴스를 구성합니다.
- 테스트 케이스를 작성하여 예상되는 모의 행동을 설정하고 메소드를 호출한 후 그에 따른 출력을 확인합니다.
견본
@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));
}
}
보다시피, 우리는 여기서 약간의 과잉 테스트를 합니다.
- 가장 관련성이 높은 검사는 두 번째 호출이 첫 번째 개체를 반환하는 것이라고 생각합니다.캐싱이 바로 그런 것입니다.키가 같은 첫 번째 두 호출은 동일한 개체를 반환하는 반면 키가 다른 세 번째 호출은 저장소에서 두 번째 실제 호출로 이어집니다.
- 캐시가 첫 번째 키에 대한 값을 실제로 가지고 있는지 확인하여 테스트 케이스를 강화합니다.실제 값을 확인하기 위해 그것을 확장할 수도 있습니다.다른 한편으로는 응용 프로그램 수준의 동작보다는 메커니즘의 내부를 더 많이 테스트하는 경향이 있기 때문에 이를 피하는 것도 괜찮다고 생각합니다.
키 테이크어웨이
- 컨테이너 동작을 테스트하기 위한 인프라가 필요하지 않습니다.
- 테스트 케이스를 쉽게 설정할 수 있습니다.
- 잘 설계된 구성요소를 사용하면 간단한 테스트 사례를 작성할 수 있고 테스트에 필요한 통합 작업이 적습니다.
Oliver의 예를 이용하여 제 앱에서 캐시 동작을 테스트해 보았습니다.제 경우에는 제 캐시가 서비스 계층에 설정되어 있고 제 레포가 올바른 횟수만큼 호출되고 있는지 확인하고 싶습니다.저는 모의실험 대신에 모의실험을 하고 있습니다.저는 먼저 실행되는 테스트가 캐시를 채우고 다른 테스트에 영향을 미친다는 것을 깨닫기 전까지 테스트가 실패하는 이유를 파악하는 데 시간을 보냈습니다.모든 테스트의 캐시를 지운 후 예상대로 동작하기 시작했습니다.
제가 생각한 바는 이렇습니다.
@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
}
}
언급URL : https://stackoverflow.com/questions/24221569/how-to-test-springs-declarative-caching-support-on-spring-data-repositories
'programing' 카테고리의 다른 글
Macos에서 mariadb를 시작하는 데 문제가 있음 (0) | 2023.09.16 |
---|---|
왜 이 PHP 스크립트(AJAX에 의해 호출됨)가 무작위로 SESSION을 로드하지 않습니까? (0) | 2023.09.16 |
JDBC VS 동면 (0) | 2023.09.16 |
"유명화�새"와 같이 데이터베이스를 마이그레이션한 후 알 수 없는 캐릭터 (0) | 2023.09.16 |
BatchUpdateException: 배치가 종료되지 않습니다. (0) | 2023.09.16 |