Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test expiration conditions?

I'm developing a component that provides tokens that have a limited lifespan and need not be persisted in case of application restart. Hence, the tokens are only stored in memory. Each token is identified by a key and have an associated time-to-live after which the token expires and can no longer be used.

I halted to think how would you go about testing that the expiration conditions work as planned?

  1. The naïve implementation using System.currentTimeMillis does not provide a proper way to inject arbitary times to test the boundaries.
  2. Adding a datetime parameter (Calendar, Joda-Time's DateTime, etc) to the appropriate methods does not make sense outside of the testing context as it's not the responsibility of the caller to know what time it is.
  3. Wrapping the system time in a class that can be injected and mocked seems the way to go but feels a bit cumbersome. This would also require another constructor as a default implemention suffice in all but testing circumstances.
  4. Defining the lifespan as a setting would allow making the wait short enough to make a sleep() or a busy-wait a reasonable and reliable solution

I'm currently leaning towards option 4 but I'd learn about more options as well!

Overly simplified scribbled scenario:

private static final long LIFE_SPAN = 60000;
private Map<T, Long> bestBeforeDates = new HashMap<T, Long>();

public void add(final T key) {
   bestBeforeDates.put(key, System.currentTimeMillis() + LIFE_SPAN);
}

public boolean isExpired(final T key) {
  // How to make this fail without waiting for x milliseconds
  return bestBeforeDates.get(key) < System.currentTimeMillis();    
}
like image 619
Aleksi Yrttiaho Avatar asked Dec 11 '25 15:12

Aleksi Yrttiaho


1 Answers

Option 3 is the best IMO. Getting the current time is a service like any other, IMO. It's easy to write a Clock interface and then fake it appropriately. If you're using a decent DI framework you don't need another constructor - just bind the a system implementation to the Clock interface and no further changes are required.

I've used this approach in a number of places and never regretted it. Of course, it depends whether you're already using dependency injection elsewhere - if it's the only dependency you'd be injecting, it's more of a pain.

like image 117
Jon Skeet Avatar answered Dec 14 '25 05:12

Jon Skeet