DEV Community

Cover image for Timetracer v1.4: Native Django Support and Easiest pytest Integration
Ujwal Vanjare
Ujwal Vanjare

Posted on

Timetracer v1.4: Native Django Support and Easiest pytest Integration

A few weeks ago, I introduced Timetracer in my previous post: "I Got Tired of 'It Works on My Machine' So I Built a Time-Travel Debugger".

The tool allows you to record API interactions (including dependencies like databases and external APIs) and replay them locally to debug production issues.

The initial version focused on FastAPI and Flask. However, the most frequent feedback I received was the need for Django support and better integration with pytest suites.

I just released v1.4.0, which adds both.

Here is why this matters and how it works.

Why This Matters

1. No More "Enterprise Setup" Pain

Django is often used for large, mature applications. These apps tend to have complex dependencies, specific database states, VPN-locked APIs, or legacy SOAP services that are a pain to run locally.

With Timetracer's new middleware, you can record a session from a staging environment (or a colleague's machine that actually works) and replay it on your machine. You get the full app behavior without needing the full enterprise infrastructure running on localhost.

2. Tests That Don't Lie

We've all written tests with unittest.mock where we guess what the API returns. Then the API changes, our mock stays the same, the test passes, but production breaks.

The new pytest integration replaces brittle mocks with real, recorded interactions. Your tests run against actual data snapshots, making them far more reliable than manual mocks.


Django Support

Timetracer now includes middleware that works with Django 3.2 LTS and newer. It supports both standard synchronous views and the newer async views in Django 4.1+.

The integration captures:

  • Incoming HTTP requests
  • Outbound API calls (requests, httpx, aiohttp)
  • Database queries (via SQLAlchemy for now, native ORM soon)
  • Redis operations

Setting it up

First, install the package with Django dependencies:

pip install timetracer[django,requests]
Enter fullscreen mode Exit fullscreen mode

Then add the middleware in your settings.py. It usually works best near the top of the list so it can capture everything:

# settings.py

MIDDLEWARE = [
    'timetracer.integrations.django.TimeTracerMiddleware',
    # ... other middleware
]

# Optional configuration
TIMETRACER = {
    'MODE': 'record',  # 'record', 'replay', or 'off'
    'CASSETTE_DIR': './cassettes',
}
Enter fullscreen mode Exit fullscreen mode

If your app makes external API calls, you should enable the plugins in your AppConfig or settings.py:

# myapp/apps.py or settings.py
from timetracer.integrations.django import auto_setup

# Enable recording for the requests library
auto_setup(plugins=['requests'])
Enter fullscreen mode Exit fullscreen mode

Workflow

  1. Run your server: python manage.py runserver
  2. Browse your site. Timetracer saves JSON "cassettes" for each request.
  3. To debug a specific request later, restart with TIMETRACER_MODE=replay.
  4. The server will now mock all external calls using the recorded data.

pytest Integration

Previously, using recorded cassettes in tests required manual setup. v1.4.0 adds a pytest plugin that automatically registers fixtures when you install the package.

This allows you to write integration tests that don't need live external APIs but still exercise your full stack.

Using the Fixtures

The plugin provides three main fixtures:

1. timetracer_replay
Use this to run a test against a pre-recorded session. It guarantees the test runs exactly the same way every time.

def test_user_profile(timetracer_replay, client):
    # 'user_profile.json' contains the recorded interaction
    with timetracer_replay("cassettes/user_profile.json"):
        response = client.get("/api/users/123")
        assert response.status_code == 200
        assert response.json()['username'] == 'testuser'
Enter fullscreen mode Exit fullscreen mode

2. timetracer_record
Use this when writing a new test case. It captures the interaction so you can save it as a cassette.

def test_new_feature(timetracer_record, client):
    # This will execute real network calls and save the result
    with timetracer_record("cassettes/new_feature.json"):
        response = client.get("/api/new-feature")
        assert response.status_code == 200
Enter fullscreen mode Exit fullscreen mode

3. timetracer_auto
This is useful for TDD. If the cassette doesn't exist, it records. If it does exist, it replays.

def test_checkout_flow(timetracer_auto, client):
    # Records first time, replays subsequently
    with timetracer_auto("cassettes/checkout.json"):
        response = client.post("/api/checkout", {'cart_id': 'abc'})
        assert response.status_code == 200
Enter fullscreen mode Exit fullscreen mode

Async Support (aiohttp)

We also added a plugin for aiohttp. Building high-concurrency apps often requires async HTTP clients, and aiohttp is a popular choice alongside httpx.

Timetracer now correctly intercepts and records aiohttp.ClientSession requests, capturing the full async flow.

What's Next

The goal remains effective local debugging. If you are using Django or pytest, I'd appreciate you trying this out and letting me know if it helps simplify your debugging workflow.

Repositories and Docs:

Top comments (0)