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.

Medium Python 3.9+ Aug 9, 2026 A/B testing & experimentation 17 views 0 copies

Python code

43 lines
Python 3.9+
import 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

stdout
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

  1. Use `freezegun` library to freeze time with decorators like `@freeze_time('2020-01-01')`.
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from A/B testing & experimentation

Related tutorials and quizzes for this topic.