Injectable Clock For Tests
Abstract time behind a Clock protocol, provide a fake clock that can advance instantly, and drive a TTL cache without sleeping.
Code
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from typing import Protocol
class Clock(Protocol):
def now(self) -> datetime: ...
class SystemClock:
def now(self) -> datetime:
return datetime.now(timezone.utc)
class FakeClock:
def __init__(self, start: datetime) -> None:
if start.tzinfo is None:
raise ValueError("start must be timezone aware")
self._current = start
def now(self) -> datetime:
return self._current
def advance(self, seconds: float) -> None:
self._current += timedelta(seconds=seconds)
@dataclass
class CachedValue:
clock: Clock
ttl: timedelta
value: str | None = None
expires_at: datetime | None = None
def get(self) -> str | None:
now = self.clock.now()
if self.value is None or self.expires_at is None or now >= self.expires_at:
return None
return self.value
def put(self, value: str) -> None:
self.value = value
self.expires_at = self.clock.now() + self.ttl
def main() -> None:
clock = FakeClock(datetime(2026, 10, 21, 12, 0, tzinfo=timezone.utc))
cache = CachedValue(clock=clock, ttl=timedelta(seconds=30))
cache.put("fresh")
print("immediately:", cache.get())
clock.advance(29)
print("after 29s:", cache.get())
clock.advance(2)
print("after 31s:", cache.get())
print("system clock tz:", SystemClock().now().tzinfo)
if __name__ == "__main__":
main()Report a problem with this exampleOpens a pre-filled issue on GitHub.