6

I am trying to add Caching support to my project so that static data is cached and database is not contacted everytime static data is needed

My applicationContext.xml looks like

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/cache
       http://www.springframework.org/schema/cache/spring-cache-3.1.xsd">
    <context:component-scan base-package="com.yahoo.comma"/>
    <bean id="liquibase" class="liquibase.integration.spring.SpringLiquibase">
        <property name="dataSource" ref="dataSource"/>
        <property name="changeLog" value="classpath:liquibase/changelog.xml"/>
        <property name="defaultSchema" value="pryme"/>
    </bean>
    <cache:annotation-driven/>

    <!-- generic cache manager -->
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="targetingAttributes"/>
            </set>
        </property>
    </bean>
</beans>

and my AgeRepository class looks like

@Component
@Transactional
public class AgeRepositoryService {
    private static final Logger LOGGER = LoggerFactory.getLogger(AgeRepositoryService.class);
    private AgeRepository ageRepository;

    @SuppressWarnings("UnusedDeclaration")
    public AgeRepositoryService() {
    }

    @Autowired
    public AgeRepositoryService(@Nonnull final AgeRepository ageRepository) {
        this.ageRepository = ageRepository;
    }

    @Nonnull
    public Age save(@Nonnull final Age age) {
        LOGGER.debug("adding age {}", age);
        return ageRepository.saveAndFlush(age);
    }

    @Cacheable("targetingAttributes")
    @Nonnull
    public List<Age> getAges() {
        return ageRepository.findAll();
    }
}

I have Integration tests that test via hitting REST endpoints that data is received.

Question
But how to I test that Caching is indeed working as expected? any advices?

daydreamer
  • 87,243
  • 191
  • 450
  • 722
  • 2
    Possible duplicate of [How to test Spring's declarative caching support on Spring Data repositories?](http://stackoverflow.com/questions/24221569/how-to-test-springs-declarative-caching-support-on-spring-data-repositories) – E-Riz Nov 20 '15 at 17:04
  • How to test no-argument method cacheable scenario in spring Duplicate of this https://stackoverflow.com/questions/50519570/spring-boot-2-testing-cacheable-with-mockito-for-method-without-arguments-is? – Gowrav Oct 01 '21 at 19:31

2 Answers2

1

You could just log every object creation. Cached objects should not be created again.

public Age(){
    System.out.println("Age created"); // or use static int and count instances
}

the other option is to create your own ConcurrentMapCacheFactoryBean. Implementation you can find on github

public class MYConcurrentMapCacheFactoryBean implements FactoryBean<ConcurrentMapCache>, BeanNameAware, InitializingBean{

   // code from github
   public String toString(){
       // todo show content of ConcurrentMapCache 
   }
}

Finally change the bean definition in your applicationContext.xml to package.MYConcurrentMapCacheFactoryBean

I think the best way to show the content of your cache is to get the cache instance using ApplicationContext:

@Autowired
private ApplicationContext appContext;

public void printCacheContent(){

     SimpleCacheManager cacheMng = (SimpleCacheManager) appContext.getBean("cacheManager");
     System.out.println(cacheMng.loadCaches().toString());
}
alex
  • 8,904
  • 6
  • 49
  • 75
0

Use an Aspect to intercept the Repository and count the number of invocations. This aspect can be activated only for Tests using Spring Profile.

e.g.

@Aspect
@Component
@Profile("intercept-age-repository")
public class AgeRepositoryInterceptor {
private AtomicInteger findAllCounter = new AtomicInteger(0);

@Around("execution(* com.package.AgeRepository.findAll(..))")
public Object findAllInterceptor(ProceedingJoinPoint originalMethod) throws Throwable {
    findAllCounter.incrementAndGet();
    return originalMethod.proceed();
}

public int getFindAllInvocationCount(){
    return findAllCounter.get();
}
}

You can then assert on the counter values for validating if the value is returned from the cache for when you use the Service instance.

Vishal
  • 381
  • 3
  • 10