복붙노트

[SPRING] 스프링 데이터 저장소에서 Spring의 선언적 캐싱 지원을 테스트하는 방법은 무엇입니까?

SPRING

스프링 데이터 저장소에서 Spring의 선언적 캐싱 지원을 테스트하는 방법은 무엇입니까?

org.springframework.data.jpa.repository.JpaRepository를 확장 한 Spring 데이터 저장소 인 MemberRepository 인터페이스를 개발했습니다. MemberRepository에는 다음과 같은 메소드가 있습니다.

@Cacheable(CacheConfiguration.DATABASE_CACHE_NAME)
Member findByEmail(String email);

결과는 Spring 캐시 추상화 (ConcurrentMapCache가 지원)에 의해 캐시됩니다.

내가 가진 문제는 결과가 db에서 처음으로 검색되고 캐시에서 두 번째로 검색된다고 주장하는 통합 테스트 (hsqldb에 대해)를 작성하려고한다는 것입니다.

처음에는 jpa 인프라 스트럭처 (엔티티 관리자 등)를 조롱하던 중 엔티티 관리자가 두 번째 호출되지 않는다고 주장하지만 너무 어렵거나 복잡해 보입니다 (https://stackoverflow.com/a/23442457/536299 참조). ).

누군가가 @Cacheable 주석이 달린 Spring Data Repository 메소드의 캐싱 동작을 테스트하는 방법에 대한 조언을 제공해 줄 수 있습니까?

해결법

  1. ==============================

    1.캐싱과 같은 기술적 측면을 테스트하려면 데이터베이스를 전혀 사용하지 마십시오. 여기에서 테스트 할 내용을 이해하는 것이 중요합니다. 매우 동일한 인수를 사용하여 호출에 대해 메소드 호출을 피할 수 있습니다. 데이터베이스를 다루는 리파지토리는이 주제와 완전히 직각 인 측면입니다.

    캐싱과 같은 기술적 측면을 테스트하려면 데이터베이스를 전혀 사용하지 마십시오. 여기에서 테스트 할 내용을 이해하는 것이 중요합니다. 매우 동일한 인수를 사용하여 호출에 대해 메소드 호출을 피할 수 있습니다. 데이터베이스를 다루는 리파지토리는이 주제와 완전히 직각 인 측면입니다.

    다음은 내가 권하는 것입니다.

    @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));
      }
    }
    

    보시다시피, 우리는 여기서 약간의 테스트를 거칩니다.

  2. ==============================

    2.올리버의 예를 사용하여 내 앱에서 캐시 동작을 테스트 해 보았습니다. 내 경우에는 캐시가 서비스 계층에 설정되어 있고 내 repo가 ​​올바른 횟수만큼 호출되고 있는지 확인하고자합니다. 나는 mockito 대신 spock mock을 사용하고 있습니다. 처음에 테스트를 실행하면 캐시가 채워지고 다른 테스트가 실행된다는 것을 알게 될 때까지 내 테스트가 실패하는 이유를 알아 내려고 노력했습니다. 모든 테스트마다 캐시를 ​​지운 다음 예상대로 작동하기 시작했습니다.

    올리버의 예를 사용하여 내 앱에서 캐시 동작을 테스트 해 보았습니다. 내 경우에는 캐시가 서비스 계층에 설정되어 있고 내 repo가 ​​올바른 횟수만큼 호출되고 있는지 확인하고자합니다. 나는 mockito 대신 spock mock을 사용하고 있습니다. 처음에 테스트를 실행하면 캐시가 채워지고 다른 테스트가 실행된다는 것을 알게 될 때까지 내 테스트가 실패하는 이유를 알아 내려고 노력했습니다. 모든 테스트마다 캐시를 ​​지운 다음 예상대로 작동하기 시작했습니다.

    여기 내가 끝까지 된 것은 :

    @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
      }
    } 
    
  3. from https://stackoverflow.com/questions/24221569/how-to-test-springs-declarative-caching-support-on-spring-data-repositories by cc-by-sa and MIT license