CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/fake-clock-testing

Fake clocks / freeze time in tests across every mainstream runtime: freezegun (Python), Jest fake timers + Sinon @sinonjs/fake-timers (JS/TS), timecop (Ruby), java.time.Clock / InstantSource injection (JVM), .NET TimeProvider / FakeTimeProvider, and libfaketime (LD_PRELOAD for any native binary). Covers the language-agnostic discipline - inject or patch the clock, freeze vs tick vs advance vs set-system-time semantics, teardown so fake clocks never leak between tests - plus the shared anti-pattern table (real sleep under a frozen clock, leaked clock state, timezone-dependent assertions). Per-library setup, API, and CI recipes live in references/{python,js,ruby,jvm,dotnet,libfaketime}.md. Use when tests need deterministic control of now(), timers, or timeouts in any language, or when choosing the right fake-clock tool for a stack.

93

Quality

93%

Does it follow best practices?

Impact

Average score across 3 eval scenarios

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

python.mdreferences/

Python - freezegun

freezegun patches datetime.datetime, datetime.date, time.time, time.gmtime, time.localtime, time.strftime, and asyncio time across the test scope. Per github.com/spulec/freezegun.

Install

pip install freezegun

Decorator (most common)

from freezegun import freeze_time
from datetime import datetime

@freeze_time("2026-05-20T14:30:00")
def test_today_is_may_20():
    assert datetime.now().strftime("%Y-%m-%d") == "2026-05-20"

Context manager and manual start/stop

with freeze_time("2026-05-20T14:30:00"):
    assert datetime.now().strftime("%Y-%m-%d") == "2026-05-20"

freezer = freeze_time("2026-05-20T14:30:00")
freezer.start()
try:
    ...
finally:
    freezer.stop()

Tick mode and moving mid-test

@freeze_time("2026-05-20T14:30:00", tick=True)   # real time passes from the frozen start
def test_clock_advances():
    t1 = datetime.now()
    t2 = datetime.now()
    assert t2 > t1

@freeze_time("2026-05-20T14:30:00")
def test_advance_one_day(freezer):
    freezer.move_to("2026-05-21T14:30:00")       # or freezer.tick(delta=timedelta(hours=24))
    assert datetime.now().day == 21

Timezone offset

@freeze_time("2026-05-20T14:30:00", tz_offset=-5)
def test_eastern_time():
    assert datetime.utcnow().hour == 19   # 14:30 + 5
    assert datetime.now().hour == 14      # wall clock

tz_offset is a fixed offset and does not know about DST. For local-zone behaviour at a transition, freeze UTC and read through zoneinfo:

from zoneinfo import ZoneInfo

@freeze_time("2026-03-08T07:30:00")   # 02:30 EST or 03:30 EDT depending on resolution
def test_spring_forward_handling():
    ny = datetime.now(ZoneInfo("America/New_York"))
    # assert against the documented library behaviour per dst-transition-reference

Async support

@freeze_time("2026-05-20T14:30:00")
async def test_async_now():
    await asyncio.sleep(0)
    assert datetime.now().strftime("%Y") == "2026"

Per freezegun docs: "freezegun is compatible with asyncio."

CI integration

jobs:
  python-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-python@v5
      - run: pip install -e ".[test]" freezegun
      - run: pytest tests/

Anti-patterns

Anti-patternWhy it failsFix
freeze_time("2026-05-20") (date only)Interpreted as midnight local; subtleUse a full ISO datetime
time.sleep(...) inside a frozen blockSleep is real-time; frozen clock doesn't advanceUse freezer.tick()
Mock datetime.utcnow separatelyConflicts with freezegunLet freezegun patch both
Forget freezer cleanup in fixturesCross-test contaminationUse decorator or with
DST test without tz_offset or zoneinfoResult is UTC; misses local behaviourCombine with zoneinfo
@freeze_time on a class without decorate_class=TrueMethods not patchedUse the class decorator explicitly

Limitations

  • C extensions bypass freezegun - a library calling clock_gettime() from C sees the real clock. Use libfaketime (libfaketime.md).
  • tz_offset doesn't know about DST - use datetime.now(tz=zoneinfo.ZoneInfo(...)) for accurate local-zone tests.
  • Module-level from datetime import datetime at import time can cache the unfrozen callable before the patch lands.

References

SKILL.md

tile.json