1

Recently in my project I was asked to use Spring's @Cacheable annotation for one of the method which returns static referenceData from the database. I followed this blog https://medium.com/@d.lopez.j/configuring-multiple-ttl-caches-in-spring-boot-dinamically-75f4aa6809f3 which guides to have caches created dynamically. I am trying to test this implementation by following this SO answer How to test Spring's declarative caching support on Spring Data repositories? and I am facing issues. I am not able to load the properties from the application-test.properties into my test class.

My CachingConfig Class

package org.vinodh.testing;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.github.benmanes.caffeine.cache.Caffeine;

import lombok.Data;

@Configuration
@ConfigurationProperties(prefix = "caching")
@Data
public class CachingConfig {

    private Map<String, CacheSpec> specs;

    @Data
    public static class CacheSpec {
        private int minutesToExpire;
        private int maximumSize;

        public int getMaximumSize() {
            return maximumSize;
        }

        public void setMaximumSize(int maximumSize) {
            this.maximumSize = maximumSize;
        }

        public int getMinutesToExpire() {
            return minutesToExpire;
        }

        public void setMinutesToExpire(int minutesToExpire) {
            this.minutesToExpire = minutesToExpire;
        }

    }

    public Map<String, CacheSpec> getSpecs() {
        return specs;
    }

    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        if (specs != null) {
            List<CaffeineCache> caches = specs.entrySet().stream()
                    .map(entry -> buildCache(entry.getKey(), entry.getValue())).collect(Collectors.toList());
            cacheManager.setCaches(caches);
        }
        return cacheManager;
    }

    private CaffeineCache buildCache(String name, CacheSpec specs) {
        Caffeine<Object, Object> caffeineBuilder = Caffeine.newBuilder()
                .expireAfterWrite(specs.getMinutesToExpire(), TimeUnit.MINUTES).maximumSize(specs.getMaximumSize());
        return new CaffeineCache(name, caffeineBuilder.build());
    }
}

My Test Class

package org.vinodh.testing;

import java.util.Map;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
@ConfigurationProperties(prefix = "caching")
@ActiveProfiles("test")
@Profile("test")
public class CachingConfigTest {

    @Configuration
    @EnableCaching
    static class NestedCacheConfiguration {

    static class CacheSpec {
            @SuppressWarnings("unused")
            private int minutesToExpire;
            @SuppressWarnings("unused")
            private int maximumSize;
        }

        private Map<String, CacheSpec> specs;

        public Map<String, CacheSpec> getSpecs() {
            return specs;
        }

        @Bean
        public CacheManager cacheManager() {
            SimpleCacheManager cacheManager = new SimpleCacheManager();
            System.out.println(getSpecs()); // Is null
            return cacheManager;
        }

    }

    @Test
    public void test() {
        System.out.println("Inside Test");
    }     
}

application-test.properties

caching.specs.test.minutesToExpire=10
caching.specs.test.maximumSize=10
Beginner
  • 335
  • 2
  • 6
  • 17
  • 1
    Have you tried adding an `addSpec` method? – daniu Oct 09 '19 at 20:42
  • @daniu Nope I didn't try that. My config class I was able to load the properties and create the caches dynamically, I also wanted to try the same using the test class – Beginner Oct 09 '19 at 20:44
  • @daniu Tried adding a method to add specs and I am getting the cache. But I wanted to have them done dynamically from my `application-test.properties` file – Beginner Oct 09 '19 at 21:19
  • 1
    Why are you replicating all the classes in your test instead of reusing the classes? Your existing configuration class will be used and the properties will be read from the `application-test.properties`. Ditch your `NestedCacheConfiguration` and you will have your caching setup already. Also remove `@Profile` and `@ConfigurationProperties` those aren't needed on your test class. – M. Deinum Oct 10 '19 at 05:46
  • As a side note, why are you declaring getter methods for classes annotated with `@Data`? They will get generated, that's the point of that annotation. – daniu Oct 10 '19 at 06:30

0 Answers0