How to Mock Time for Cache TTL Testing in Python
This code demonstrates how to test a cache's TTL expiration logic by mocking time.time with unittest.mock to control the passage of time.
Python code
43 linesimport time
from unittest.mock import patch
class ConfigCache:
def __init__(self, ttl=60):
self.ttl = ttl
self._store = {}
self._timestamps = {}
def get(self, key):
if key not in self._store:
return None
if time.time() - self._timestamps[key] > self.ttl:
del self._store[key]
del self._timestamps[key]
return None
return self._store[key]
def set(self, key, value):
self._store[key] = value
self._timestamps[key] = time.time()
def invalidate(self, key):
self._store.pop(key, None)
self._timestamps.pop(key, None)
if __name__ == "__main__":
with patch("__main__.time.time") as mock_time:
cache = ConfigCache(ttl=10)
mock_time.return_value = 1000.0
cache.set("db_host", "localhost")
mock_time.return_value = 1005.0
assert cache.get("db_host") == "localhost"
mock_time.return_value = 1011.0
assert cache.get("db_host") is None # TTL expired
cache.set("api_key", "secret")
cache.invalidate("api_key")
assert cache.get("api_key") is None
print("All cache tests passed with mock time control")
Output
All cache tests passed with mock time control
How it works
The ConfigCache class stores key-value pairs along with timestamps to track when each entry was last set. When get is called, the current time is compared with the stored timestamp; if the difference exceeds ttl, the entry is removed and None is returned. By patching time.time with a mock function, you can simulate precise time advances without sleeping. This deterministic control is essential for testing TTL-based behavior in unit tests, as real time delays would make tests slow and flaky.
Common mistakes
- Forgetting to patch `time.time` at the correct namespace, e.g., `__main__.time.time` when the module is run directly.
- Not resetting the mock's return value between scenarios, leading to unexpected cache hits or misses.
- Assuming real time passes during tests; always mock time for reproducible TTL tests.
Variations
- Use `freezegun` library to freeze time with decorators like `@freeze_time('2020-01-01')`.
- Implement the TTL comparison inside a separate method to isolate the time dependency for easier mocking.
Real-world use cases
- Verifying that configuration caches auto-refresh after expiry in production services.
- Testing distributed cache invalidation logic where TTL values are configured per tenant.
- Simulating session expiration in authentication systems without waiting for real time.
Sponsored
More from A/B testing & experimentation
Keep learning
Related tutorials and quizzes for this topic.