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.