0

I'm trying to test my LoginCacheServiceImpl but having problems. I followed this tutorial http://crunchify.com/how-to-create-a-simple-in-memory-cache-in-java-lightweight-cache/ and called it InMemoryCache with my own model object LoginEvent.

@Service
@PropertySource("classpath:app.properties")
public class LoginCacheServiceImpl {

    @Value("$app{timeToLive}")
    private long timeToLive;

    @Value("$app{timerInterval}")
    private long timerInterval;

    private InMemoryCache<String, LoginEvent> cache;

    @Override
    public InMemoryCache<String, LoginEvent> getCache() {
        return cache;
    }

    @PostConstruct
    public void init() {
        cache = new InMemoryCache<>(timeToLive, timerInterval, 10000);
    }
}

The code runs fine but I'm trying to write unit tests for it. In the test class:

@TestPropertySource("classpath:app-test.properties")
@ContextConfiguration(classes = { LoginCacheServiceImplTest.class, LoginCacheServiceImpl.class })
@RunWith(SpringJUnit4ClassRunner.class)
public class LoginCacheServiceImplTest
    @Autowired
    private Environment environment;

    private LoginCacheServiceImpl cacheService;

    @Before
    public void setUp() {
        String test = environment.getProperty("timeToLive");    // this value gets parsed correctly
        cacheService = new LoginCacheServiceImpl();             
    }

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

I get the error:

Caused by: org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'long'; nested exception is java.lang.NumberFormatException: For input string: "$app{timeToLive}"
    at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:77)
    at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:54)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:968)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:949)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
    ... 42 more
Caused by: java.lang.NumberFormatException: For input string: "$app{timeToLive}"

I'm not sure how I should either rewrite my LoginCacheServiceImpl to make it testable, or preferably, to set up my test class so PostConstruct parses the timeToLive and timerInterval properly.

app-test.properties:

timeToLive=1000
timerInterval=1000

As an aside, I'm using Mockito 2.X. Can you use both MockitoJUnitRunner and SpringJUnit4ClassRunner? If not, how do you choose which one to use (sorry, new to Java).

Edit: If I have this line

@ContextConfiguration(classes = { LoginCacheServiceImplTest.class, LoginCacheServiceImpl.class })

the unit test gives me that numbeformatexception error when I try to run the test as it is trying to create LoginCacheServiceImpl. But if I change it to

@ContextConfiguration(classes = { LoginCacheServiceImplTest.class })

and just do

String prop = environment.getProperty("timeToLive");

where in my app-test.properties file has

timeToLive=1000

the unit test can read that properties file. The timeToLive=1000 is a different number than I have in the original app.properties file so I do not think it's an issue of finding the app-test.properties file.

Crystal
  • 28,460
  • 62
  • 219
  • 393
  • 1
    What is the content of app-test.properties – Amit Mahajan Sep 13 '17 at 01:51
  • It just has timeToLive and timerInterval in it. timeToLive=1000 I assume it's fine since I can read the value without instantiaring the logincacheservice fine. – Crystal Sep 13 '17 at 02:06
  • It appears the test run is not able to read or locate this file based on the error you mentioned. This is already discussed here: https://stackoverflow.com/questions/17353327/populating-spring-value-during-unit-test – Amit Mahajan Sep 13 '17 at 14:26

1 Answers1

0

Please check the below updated class:

@Service
@PropertySource("classpath:app.properties")
public class LoginCacheServiceImpl {

    @Value("${timeToLive}")
    private long timeToLive;

    @Value("${timerInterval}")
    private long timerInterval;

    private InMemoryCache<String, LoginEvent> cache;

    @Override
    public InMemoryCache<String, LoginEvent> getCache() {
        return cache;
    }

    @PostConstruct
    public void init() {
        cache = new InMemoryCache<>(timeToLive, timerInterval, 10000);
    }
}

if your propertySource able to find app.properties on classpath correctly, you can use ${keyname} to access the property. Environment object already have properties configured by spring container as it extend PropertyResolver. check the docs for the same.Environment spring

or instead of using @Value, you can autowire the @Environment and use it in your service class.

Sangam Belose
  • 4,262
  • 8
  • 26
  • 48