Skip to content

Python

A complete, copy-paste walkthrough: start a new FastAPI app, snapshot its rendered page with Dungbeetle, change a route, and read the semantic diff. It uses the default web capture with the zero-dependency fetch driver — no browser install required. A CLI variant is at the end.

Prerequisites

  • Python 3.10+ with pip and venv.
  • Node.js 22 or newer — Dungbeetle's runtime (node --version).

1. Create a new FastAPI app

Make a project, set up a virtual environment, and install FastAPI + Uvicorn:

sh
mkdir fastapi-app && cd fastapi-app
python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install fastapi uvicorn

Create main.py with a single HTML route:

python
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.get("/", response_class=HTMLResponse)
def index() -> str:
    return "<html><body><h1>Hello from FastAPI</h1></body></html>"

Run it to confirm it serves at http://127.0.0.1:8000:

sh
uvicorn main:app --port 8000

The FastAPI page in a browser at http://127.0.0.1:8000.

2. Install Dungbeetle

sh
npm install --save-dev dungbeetle
npx dungbeetle --version

This adds a package.json to the project; commit it alongside your Python code.

3. Scaffold a config

Generate a starter config, then replace it with the one below. It starts Uvicorn, waits until it responds, and snapshots the page — then shuts the server down:

sh
npx dungbeetle init
json
{
  "version": 1,
  "project": { "name": "fastapi-app" },
  "baselinesDir": "dungbeetle.snapshots",
  "artifactsDir": ".dungbeetle/artifacts",
  "lifecycle": {
    "start": ["uvicorn main:app --port 8000"],
    "wait": { "url": "http://127.0.0.1:8000", "timeoutMs": 30000 },
    "capture": [
      { "kind": "web", "name": "index", "url": "http://127.0.0.1:8000" }
    ]
  }
}

The web target uses the default fetch driver (no Playwright, no browser). See Web snapshots and the full configuration reference.

4. Validate the setup

sh
npx dungbeetle doctor

5. Capture the first baseline

sh
npx dungbeetle update

Dungbeetle boots Uvicorn, waits for http://127.0.0.1:8000, captures a structured DOM snapshot, writes it under dungbeetle.snapshots/, and stops the server. Commit the baseline:

sh
git add dungbeetle.snapshots
git commit -m "Add Dungbeetle baseline for index route"

6. Make a change and compare

Edit the heading in main.py:

python
    return "<html><body><h1>Hello from Example App</h1></body></html>"

Now compare current output against the baseline:

sh
npx dungbeetle test

test boots the server again, re-captures, and exits non-zero because the DOM changed. The diff is semantic — a node-level text-changed, not a pixel blob.

7. Read the report

sh
npx dungbeetle ci --json report.json --html report.html
open report.html   # macOS; use xdg-open on Linux

Screencast: doctor → the edited main.py fails test with a semantic diff → update accepts it → test passes.

Flow video — the terminal session replayed, then the served page with its change, then the failing run's HTML report.

8. Accept the new baseline

sh
npx dungbeetle update
git add dungbeetle.snapshots && git commit -m "Update index baseline"

Snapshot a CLI instead

For a command-line tool, capture stdout/stderr with a terminal target instead of a web one — no server needed:

json
{
  "version": 1,
  "project": { "name": "python-cli" },
  "lifecycle": {
    "capture": [
      { "kind": "terminal", "name": "help", "command": "python -m yourtool --help" }
    ]
  }
}

dungbeetle update / test then snapshot and diff the command's output, with ANSI normalized to stable structured segments.

9. Review in the cloud

Push a run to a Dungbeetle server and the review UI shows the whole story in one place: the DOM's semantic diff, the FastAPI endpoint's JSON diff with its numeric changes, and before/after screenshots from the Playwright driver.

Screencast: sign in → open the failing run → semantic diffs, screenshot comparison with onion skin → approve and promote the new baselines.

Next steps

  • Add a web target per route, or mix web and terminal targets in one config.
  • Other frameworks work the same way — point start at flask run or python manage.py runserver and set wait.url to match.
  • Need browser-rendered DOM or screenshots? Use the Playwright driver. Push runs to the cloud server to review diffs in a browser.

Source-available: CLI under FSL-1.1-ALv2, cloud server under BUSL-1.1. See Licensing.