I'm developing a component that provides tokens that have a limited lifespan and need not be 开发者_如何学Gopersisted 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?
- The naïve implementation using
System.currentTimeMillis
does not provide a proper way to inject arbitary times to test the boundaries. - Adding a datetime parameter (
Calendar
, Joda-Time'sDateTime
, 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. - 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.
- 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();
}
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.
精彩评论