How to Mock Redis EXPIRE, TTL, and PERSIST in Python
A lightweight in-memory MockRedis class that simulates Redis key expiration, TTL, and persist behavior for tests and local development.
Python code
54 linesimport time
class MockRedis:
def __init__(self):
self._store = {}
self._expiry = {}
def set(self, key, value):
self._store[key] = value
self._expiry.pop(key, None)
return True
def expire(self, key, ttl_seconds):
if key not in self._store:
return False
self._expiry[key] = time.time() + ttl_seconds
return True
def persist(self, key):
if key not in self._store:
return False
if key in self._expiry:
del self._expiry[key]
return True
return False
def ttl(self, key):
if key not in self._store:
return -2
if key not in self._expiry:
return -1
remaining = int(self._expiry[key] - time.time())
return max(0, remaining)
def get(self, key):
if key not in self._store:
return None
expire_at = self._expiry.get(key)
if expire_at is not None and time.time() > expire_at:
del self._store[key]
del self._expiry[key]
return None
return self._store[key]
if __name__ == "__main__":
r = MockRedis()
r.set("session", "abc123")
print("TTL after set:", r.ttl("session"))
print("Expire result:", r.expire("session", 5))
print("TTL after expire:", r.ttl("session"))
print("Persist result:", r.persist("session"))
print("TTL after persist:", r.ttl("session"))
print("GET value:", r.get("session"))
Output
TTL after set: -1
Expire result: True
TTL after expire: 5
Persist result: True
TTL after persist: -1
GET value: abc123
How it works
The MockRedis stores keys in a dictionary alongside a separate expiry map. set clears any prior expiry for the key. expire records an absolute timestamp using time.time() plus the TTL. persist removes the key from the expiry map, causing ttl to return -1 as Redis does. get proactively checks whether the expiry timestamp has passed and deletes the key on access. Using a mock like this lets you test TTL logic without hitting a real Redis server.
Common mistakes
- Forgetting that `ttl` returns -1 for persistent keys, not 0.
- Not deleting the expiry entry on `set`, causing stale TTL data.
- Using the TTL value directly instead of an absolute timestamp, breaking long-running tests.
- Assuming `persist` returns False for keys without TTL instead of True.
Variations
- Use `freezegun` to freeze `time.time()` for deterministic TTL tests.
- Subclass `unittest.mock.Mock` and patch Redis methods instead of building a full class.
- Use the `fakeredis` third-party library for a more complete Redis mock with Lua script support.
Real-world use cases
- Unit-testing session management code that relies on Redis TTL without spinning up a server.
- Developing locally against a Redis-like interface when Docker or Redis is unavailable.
- Simulating cache expiry behavior in integration tests to verify cache-miss and refresh paths.
Sponsored
More from Caching & Redis
- Cache Asides in Python with a Read-Through Loader easy
- Cache Data in Redis with Python easy
- Cache Penetration Null Object Mock in Python medium
- Cache Stampede Prevention with SingleFlight in Python medium
- Cache Warming with Python: Preload Hot Keys easy
- Coalescing duplicate in-flight requests: one shared result for concurrent callers hard
Keep learning
Related tutorials and quizzes for this topic.