Skip to content

angrydat/dbression

Repository files navigation

dbression — database regression testing for schema changes, migrations, and critical queries

dbression

Rage-quit your flaky DB regressions.

License: MIT Python 3.12+

Modern, lightweight database regression testing — read your existing DBFit wikis, run them anywhere. dbression is a Python re-implementation in the spirit of the fantastic DBFit Framework: your .wiki suites stay, the Java runtime, the bulky FitNesse server, and the Browser based Wiki pages are gone. Multi-DB, pytest-style CLI, JUnit XML and JSON for CI as well as a developer and agentic friendly STDOUT.

$ dbression run tests/
dbression 0.2.0 — Suite: tests @ postgresql+psycopg://foo:***@db01/bar

✓ HelloSql                                  0.004s
  CommonSuite/
    ChangelistSuite/
      ✓ AAddBasicTest                       0.027s
      ✓ BAddNormalizationTest               0.029s
      ✓ CAddWhitelistTest                   0.025s
      ✓ DAddInvalidArgsTest                 0.014s
      ✓ ERemTest                            0.052s
      ✓ FViewTest                           1.236s
  EventsSuite/
    FireSuite/
      ✓ LookupTest                          0.157s
      ✗ SchemaTest                          0.013s

══════════════════════ FAILURES ══════════════════════
EventsSuite/FloodingSuite/SchemaTest :: Query Row-Mismatch
  SQL:    SELECT column_name, data_type FROM information_schema.columns WHERE ...
  Expected:  | id | integer | … |
  Actual:    | id | integer | … | catastrophic | boolean |
                                  ^^^^^^^^^^^^

══════════════════════ SUMMARY ══════════════════════
8 passed, 1 failed in 1.56s

Why dbression?

Pain with DBFit dbression's answer
Java runtime + FitNesse server + 80 MB InstantClient One pip install, pure Python, ~30 MB
CLI shows only pass/fail counts; failures only visible in web UI Full exception, SQL, bind values, row diff in stdout
Stagnant, no recent releases Active development, modern toolchain (uv, SQLAlchemy 2.0, Typer, Rich)
One driver per DB, Oracle thick-mode only Postgres (psycopg), SQL Server (pymssql), Oracle (oracledb thin) — no native libs needed
No CI integration JUnit XML + JSON out of the box

LLM-friendly by design. Failures land as text — paste them into Claude/ChatGPT/Copilot and the model gets every detail it needs to suggest a fix, no screenshots, no context-switching.

Quickstart

# install (we use uv, but pip works too)
uv tool install dbression          # or: pipx install dbression
                                   # or: pip install dbression

# run
dbression run tests/

There's also an examples/ folder with three runnable demo suites (hello-SQL, stored-procedure-with-capture, schema-drift via Inspect Table) — each file is browsable Markdown and an executable test.

Your first test

Drop a .wiki file in a folder with a _root.wiki and a connection.properties:

tests/
├── _root.wiki
├── connection.properties
└── HelloSql.wiki

_root.wiki:

!|DatabaseEnvironment|postgres|
|ConnectUsingFile|connection.properties|

connection.properties:

service=localhost
username=postgres
password=${POSTGRES_PASSWORD}    # env-var expansion supported
database=mydb

HelloSql.wiki:

!|Query|select 'OK' as connection|
|connection|
|OK|
$ dbression run tests/
✓ HelloSql   0.004s
1 passed in 0.04s

What dbression understands

The full DBFit fixture subset that real-world suites actually use:

Fixture What it does
Query SELECT with expected rows; >>name captures values, <<name reads them back
Execute / Execute Ddl Run arbitrary SQL/PL-SQL/DDL
Execute Procedure Call a stored procedure (dialect-aware: SELECT/CALL PG, EXEC MSSQL, BEGIN…END Oracle)
Execute Procedure Expect Exception Assert a procedure raises a specific SQLSTATE or ORA code
Insert / Delete DML with column-header + value-rows, <<sym substitution, >>id capture via RETURNING
Set Parameter Set a symbol/bind variable from the wiki
Inspect Table / Inspect View / Inspect Procedure Schema introspection with optional diff against expected
Store Query / Compare Stored Queries Snapshot a query result, compare snapshots later

Plus the DBFit-isms you already rely on:

  • !- ... -! escape blocks for multi-line SQL with pipes
  • Oracle q'~ ... ~' custom quoting
  • :name (bind), <<name (cell read), _:name (text substitution before compile)
  • Nested sub-suites with _root.wiki per directory
  • SuiteSetUp / SuiteTearDown per suite, with one transaction wrapping everything
  • YAML-style front-matter for tags: --- Suites: critical NotOnCI ---

Executable Markdown: .test.md

If you'd rather never see FitNesse wiki syntax again, write your tests in plain Markdown — the same file is a readable document and a runnable test:

# Example test

<!-- dbression:env=postgres -->
<!-- dbression:connection=conn.properties -->

### Query

```sql
select count(*) as cnt from wlk.app_selectset where oid = 999900001
```

| cnt |
|-----|
| 0   |

### Execute Procedure pr_foo_bar

| p_oid     | p_order           |
|-----------|-------------------|
| 999900001 | ASCENDING         |

Renders in GitHub, GitLab, Obsidian, Typora, VS Code out of the box — tables look clean, SQL is syntax-highlighted, directives are invisible (HTML comments). Conventions:

  • ### <FixtureName> [args…] starts a fixture table (longest-prefix match against the registry)
  • A fenced ```sql … ``` block directly below becomes the SQL argument for Query / Execute / Store Query
  • The next Markdown table → column headers + data rows, with full DBFit idiom support (>>capture, <<read, :bind)
  • <!-- dbression:env=… -->, <!-- dbression:connection=… -->, <!-- dbression:tags critical, NotOnCI --> carry suite directives

Built-in migration tool:

dbression convert path/to/wiki-suite/    # mirrors the directory, generates a .test.md next to each .wiki
dbression convert one-file.wiki -o out.test.md

.wiki and .test.md may coexist — the Markdown file wins at runtime (newer format). That lets you migrate one suite at a time.

CI Integration

dbression writes JUnit-XML (the universal CI lingua franca) and JSON (rich, for tooling) side by side. Pick one or both.

Bitbucket Pipelines

pipelines:
  default:
    - step:
        name: dbression
        image: python:3.12
        script:
          - pip install uv
          - uv sync
          - uv run dbression run tests/ \
              --junit-xml test-reports/dbression-junit.xml \
              --json test-reports/dbression.json
        artifacts:
          - test-reports/**

Bitbucket scans test-reports/*.xml automatically and renders test results in the pipeline UI. No plugin needed.

Jenkins (declarative)

pipeline {
  agent any
  stages {
    stage('dbression') {
      steps {
        sh 'uv run dbression run tests/ --junit-xml test-reports/dbression-junit.xml --json test-reports/dbression.json'
      }
    }
  }
  post {
    always {
      junit 'test-reports/dbression-junit.xml'
      archiveArtifacts 'test-reports/dbression.json'
    }
  }
}

GitLab CI

dbression:
  script:
    - uv run dbression run tests/ --junit-xml test-reports/dbression-junit.xml
  artifacts:
    reports:
      junit: test-reports/dbression-junit.xml

CLI cheat sheet

dbression run <suite-path>                   # run an entire (sub-)suite
dbression run <path> -v                      # show every fixture table, not just the page line
dbression run <path> --tag critical          # only run pages tagged `critical`
dbression run <path> --skip-tag NotOnCI      # skip pages tagged `NotOnCI`
dbression run <path> --commit-mode page      # DBFit-style: commit per page (default: rollback per test)
dbression run <path> --junit-xml report.xml  # CI report
dbression run <path> --json report.json      # programmatic / LLM consumption
dbression convert path/to/wiki/              # converts .wiki → .test.md (in-place)
dbression convert file.wiki -o out.test.md   # single file with explicit output path
dbression version

Status

dbression is WIP but already useful — running production-scale DBFit suites today across Postgres, Oracle and SQL Server. Honest state of the parts:

Component Status
Wiki parser (DBFit subset) ✅ verified
Fixtures (Query, Execute, Insert, Delete, Set Parameter, Execute Procedure, Inspect *, Store/Compare Query, Update)
PostgreSQL ✅ verified against a real-world suite
Oracle ✅ verified against a real-world 19c suite via oracledb (thin)
SQL Server ✅ verified against a real-world suite via pymssql
SQLite ✅ via stdlib sqlite3 — no procedures (the DB has none)
JUnit XML + JSON output
.test.md native Markdown format + dbression convert
Plugin entry-points for custom fixtures

Don't expect perfect compatibility with every obscure DBFit feature. We promise the subset real teams actually use, with a sharper UX and a tenth of the footprint.

Contributing

dbression is small enough to read end-to-end in an afternoon. The whole thing is roughly:

src/dbression/
├── cli.py           # typer entrypoint
├── parser/          # wiki → AST
├── fixtures/        # one file per fixture family
├── db/              # connection.properties + SQLAlchemy engine factory
├── report/          # console + junit + json
├── runner.py        # transactions, savepoints, tag filtering
└── symbols.py       # capture / read / substitution

Adding a fixture is one decorator and a run() method:

from dbression.fixtures.base import Fixture, FixtureContext, FixtureResult, register

@register("My Custom Fixture")
class MyFixture(Fixture):
    def run(self, table, ctx: FixtureContext) -> FixtureResult:
        # ... use ctx.conn (SQLAlchemy Connection) and ctx.symbols
        return FixtureResult(passed=True, message="OK")

Distributing your own fixtures as a plugin

You don't have to fork dbression to add fixtures. Ship them in your own pip-installable package via an entry-point:

# your-plugin-pkg/pyproject.toml
[project.entry-points."dbression.fixtures"]
my-fixtures = "my_plugin.fixtures"

dbression discovers the entry-point at startup and imports the module — your @register decorators do the rest. For quick ad-hoc plugins without packaging:

PYTHONPATH=/path/to/plugin DBRESSION_PLUGINS=my_plugin dbression run tests/

Plugin import failures emit a warning and don't crash the run.

PRs welcome — please add a test that exercises the new behavior (we use pytest).

License

MIT — see LICENSE.