End-to-end example of BDD testing for Databricks Unity Catalog functions, using Behave and the Statement Execution API.
Tests run on a standard CI runner (GitHub Actions) β no Spark session, no local cluster, no Java. Each scenario is one HTTP call to a real UC function.
Status: community-contributed reference implementation. Not officially supported by Databricks. Issues and PRs are welcome but response times are best-effort. Use at your own risk; review the code against your own security and compliance requirements before using in production.
π Read the deep dive blog post β β the why behind this pattern, where it fits, and where it doesn't.
A Unity Catalog SQL function is the single source of truth for the business rule. Both callers reference it without duplication:
UC function (sql/)
βββ BDD test suite (tests/bdd/) β validates the contract via Statement Execution API
βββ Lakeflow SDP pipeline (pipelines/) β calls the same function in production
The BDD suite gates pipeline promotion in CI. If a code change breaks a scenario, the pipeline never runs.
- Databricks workspace with Unity Catalog enabled
- A running SQL warehouse (Serverless recommended)
- Python 3.10+,
uv, Databricks CLI (Go-based, v0.200+)
Verify CLI auth before starting:
databricks current-user medatabricks-bdd/
βββ src/
β βββ compliance_bdd/ # Python package β BDD utilities
β βββ spark_rules.py # Statement Execution API wrapper
β βββ fixtures.py # Domain β UC function argument translators
β
βββ pipelines/
β βββ compliance_pipeline.py # Lakeflow SDP source file (workspace artifact, not packaged)
β
βββ sql/
β βββ check_back_to_back_promo.sql # UC function definition β the shared contract
β
βββ scripts/
β βββ deploy_function.py # Deploys UC function to target catalog/schema
β
βββ tests/
β βββ bdd/
β βββ environment.py # behave hooks (.env loading, env var validation)
β βββ features/
β β βββ *.feature # Gherkin scenarios β human-readable rule contracts
β βββ steps/
β βββ *_steps.py # Step definitions β thin wiring between Gherkin and call_rule()
β
βββ resources/
β βββ pipeline.yml # DABs pipeline resource (Lakeflow SDP)
β βββ jobs.yml # DABs job resources
β
βββ .github/
β βββ workflows/
β βββ bdd.yml # CI: bundle deploy β BDD tests (gate) β pipeline run
β
βββ databricks.yml # Asset Bundle config with dev/staging/prod targets
βββ pyproject.toml # Python project β packages src/ only
βββ behave.ini # behave configuration
βββ Makefile # Command interface
The src/compliance_bdd/ package is the only thing that gets built into a wheel and deployed as a library dependency. The pipelines/ directory contains Lakeflow SDP source files that the pipeline runtime discovers and executes directly β they can't be installed as a package entry point. Keeping them separate makes the distinction explicit and prevents setuptools from accidentally packaging workspace artifacts.
See pyproject.toml:
[tool.setuptools.packages.find]
where = ["src"] # only package src/ β pipelines/ stays out of the wheel# 1. Clone and install
git clone https://github.com/you/databricks-bdd
cd databricks-bdd
cp .env.example .env # fill in DATABRICKS_WAREHOUSE_ID and target catalog/schema
make install
# 2. Deploy the UC function
make setup
# 3. Run the BDD suite
make testExpected output on a warm warehouse:
Feature: Back-to-Back Promotion Compliance
Rule: Products must have a minimum 4-week gap between promotions
......... 9 scenarios (9 passed)
Took 0m14.2s
The bundle has three targets. BDD_CATALOG and BDD_SCHEMA are driven by bundle variables β the test suite always points at the same catalog/schema as the deployed pipeline.
| Target | Catalog | Schema |
|---|---|---|
dev |
dev |
compliance_<your-username> |
staging |
staging |
compliance_staging |
prod |
main |
compliance |
# Validate the bundle config
make validate
# Deploy to your personal dev schema
make deploy-dev
# Deploy to staging (what CI does)
make deploy-stagingOn push to main, the workflow enforces a strict gate sequence:
bundle deploy --target staging
β deploys UC function + pipeline definition
behave (BDD gate)
β calls real UC functions in staging catalog
β green β proceed | red β stop
bundle run compliance_pipeline --target staging
β pipeline runs against validated functions
On pull requests: deploy + BDD only (pipeline run is skipped).
Add these secrets to your repository (Settings β Secrets and variables β Actions):
| Secret | Value |
|---|---|
DATABRICKS_HOST |
Your workspace URL, e.g. https://adb-xxx.azuredatabricks.net |
DATABRICKS_TOKEN |
Service principal PAT (not a personal token) |
DATABRICKS_WAREHOUSE_ID |
SQL warehouse ID from the Connection Details tab |
Grant the service principal: USE CATALOG, USE SCHEMA, EXECUTE on the target schemas, and CAN_USE on the warehouse.
- Write the SQL function in
sql/and deploy it withmake setup - Create
tests/bdd/features/<rule_name>.featurewith Gherkin scenarios - Add a fixture translator in
src/compliance_bdd/fixtures.pyif the function has a non-trivial argument shape - Add step definitions in
tests/bdd/steps/<rule_name>_steps.py - Run
make test
The production pipeline calls the same UC function β add it to pipelines/compliance_pipeline.py and it's covered by the existing BDD gate.
Each scenario is one warehouse query. The suite runs 9 scenarios β negligible at Serverless SQL pricing. A Scenario Outline with hundreds of rows adds up on a busy PR queue; consider batching via VALUES clauses if cost becomes a concern.