Skip to main content

pytest-databricks-lakeflow

Test the Lakeflow Jobs in your Databricks Asset Bundle with plain pytest: deploy a throwaway copy of a job or a single task, run it, assert on the result, tear it down.

PyPI Python versions CI Coverage Docs Licence Ruff

Why this exists

  • A job in a bundle is usually checked by deploying it and pressing Run. This turns that into a pytest test, with assert, parametrize and xdist.
  • Every test runs its own copy of the job, so tests run in parallel and never touch the jobs your bundle deployed, except the children of Run Job tasks, which the plugin warns about. With pipelines="copy", the pipelines that a job's pipeline tasks update are copied too and publish to the test's temporary schema, and lakeflow.pipeline() copies one pipeline on its own, so a pipeline change is checked without touching the deployed pipeline's tables.
  • Task values cannot be read through the Jobs API. The plugin injects tasks that set and capture them, so one task can be tested on its own with the upstream values you choose.
  • Test data needs no seed task in the bundle and no Databricks Connect. setup= and teardown= run plain Python functions with the real spark and dbutils, a list of SQL statements, or a notebook file as the first and last tasks of the copy, or with teardown_at="close" the teardown alone when the job closes, after the test's assertions.
  • bundle validate passes wiring mistakes, such as an undeclared {{job.parameters.<name>}} or a depends_on task that does not exist, that the deploy then refuses one at a time. lakeflow.check(key) lists them all from the template, without creating a job, and pytest --lakeflow-check collects one such check per bundle job.

Quickstart

You need Python 3.11 or later, the Databricks CLI v1.7.0 or later on PATH, credentials that both the CLI and the SDK can use (a ~/.databrickscfg profile or the DATABRICKS_* variables), and a workspace with serverless jobs.

uv add --dev pytest-databricks-lakeflow

Or pip install pytest-databricks-lakeflow. Add a test target to databricks.yml. Development mode pauses schedules, and the separate root_path keeps test deployments away from everyone else's:

targets:
  test:
    mode: development
    workspace:
      root_path: ~/.bundle/${bundle.name}/test

Write a test. This one runs my_example_job from the example bundle; swap in your own job and task keys:

from pytest_databricks_lakeflow import Lakeflow


def test_job_succeeds(lakeflow: Lakeflow) -> None:
    with lakeflow.job("my_example_job", params={"param1": "hello", "param2": "world"}) as job:
        run = job.run()
        run.raise_for_failure()
        assert run.task("transform").output == "hello world"
        assert "Success! hello" in run.task("my_wheel_task").logs

Run pytest. The first test deploys the bundle's test target once, then every test creates, runs and deletes its own job. A failing test keeps its job, and the job and run URLs appear in the test report. Write assert run.succeeded, run and a failure reads:

AssertionError: JobRun('my_example_job', FAILED, https://<workspace>/#job/123/run/456, params={'param1': 'forbidden', 'param2': 'world', 'mode': 'full', 'schema': ''}, failing=['transform: PermissionError: forbidden may not read /Volumes/example/raw/landing'])

run.raise_for_failure() does the same in one call. It returns the run when it succeeded and otherwise raises LakeflowRunFailed with that repr followed by one line per failing task: its full error, its root cause (the last Caused by: line, or the first exception of a Python chain) when the trace has one, and the task's run URL. The run's job parameters and each failing task's resolved parameters follow on params: lines.

pytest --lakeflow-report-dir=build/lakeflow also writes each test's runs, those the plugin cancelled included, to a report.json, with every task's state, output, error and task values, next to the raw logs and exported notebooks, so a CI build can keep them as an artefact after the jobs are deleted. pytest --lakeflow-debug-notebook writes, for each notebook task that failed in a failed test, a notebook in your workspace that sets the task's resolved parameters as widgets and runs the task's notebook, and job.debug_notebook("transform") writes one on demand.

To change the target, set a catalog for temporary schemas or lengthen the timeout, override the lakeflow fixture in conftest.py:

from datetime import timedelta

import pytest

from pytest_databricks_lakeflow import Lakeflow


@pytest.fixture(scope="session")
def lakeflow() -> Lakeflow:
    return Lakeflow(target="test", catalog="dev_sandbox", timeout=timedelta(minutes=20))

To skip cluster start-up, pass existing_cluster_id="<cluster id>" to Lakeflow: tasks that the bundle puts on job clusters then run on that running cluster instead. To keep job clusters but run each on one node, pass single_node=True; single node compute needs dedicated access mode.

Testing one task

lakeflow.task("job_key.task_key") builds a job that holds only that task. Each upstream task you name in upstream_task_values is replaced by a stub task that sets those task values, or by a Stub that also answers the task's {{tasks.<key>.output.<path>}} references with literals, and capture_task_values adds a probe task that reports the values the task set, even when it failed. upstream_from=run takes the stub values from the task values a previous run of the job captured, so a task can be replayed against real upstream data. lakeflow.task("job_key.loop_key.nested_key", input={"id": 7}) runs the task nested in a for-each task once, without its loop, with the item you choose.

import pytest

from pytest_databricks_lakeflow import Lakeflow


@pytest.mark.parametrize(("greeting", "expected"), [("hello", "hello world"), ("goodbye", "goodbye world")])
def test_transform(lakeflow: Lakeflow, greeting: str, expected: str) -> None:
    with lakeflow.task(
        "my_example_job.transform",
        params={"param1": greeting, "param2": "world"},
        upstream_task_values={"extract": {"row_count": 42, "source_path": "/Volumes/x/y/z"}},
        capture_task_values=["rows_written"],
    ) as task:
        run = task.run()
        assert run.succeeded
        assert run.task("transform").output == expected
        assert run.task("transform").task_values["rows_written"] == 42

Inside the task, dbutils.jobs.taskValues.get(taskKey="extract", key="source_path") and {{tasks.extract.values.row_count}} resolve against the stub. Reading a value the task never set raises a KeyError that says so.

How it works

  1. The first test that needs a job runs databricks bundle deploy for the target once per session and reads each deployed job's settings through the Jobs API.
  2. lakeflow.job() copies those settings into a new job named [pytest] <test id>, with schedules, triggers, notifications, run_as and the bundle marker removed and dashboard and SQL alert subscriptions paused (alert and Power BI tasks are left as they are), tagged with the run id, the test, and, when known, the CI run URL, user, commit and branch. Pass run_as="keep" to keep the template's run_as, and trigger= to give the {{job.trigger.*}} references the values a trigger would.
  3. run() starts the job, waits, and returns a JobRun with every task's state, output, logs, error and captured values.
  4. Leaving the with block deletes the job unless the test failed. Session end cancels anything still running.
  5. lakeflow.task() does the same for one task, with the stub and probe tasks on serverless compute or a job cluster you name.
  6. lakeflow.pipeline(), or pipelines="copy" on job() and task(), makes a pipeline from a deployed pipeline's spec that publishes to the schema you pass, runs it in development mode, and deletes it with its tables before the schema is dropped.

Documentation

Everything else is on the docs site: https://stevenayers.github.io/pytest-databricks-lakeflow/

  • Getting started: prerequisites, install, bundle target, first test
  • Writing job tests: parameters, only, stubbed upstream tasks and replay, simulated triggers, for-each inputs and iterations, repeated runs, failures, temporary schemas, pipeline copies, shared runs
  • Writing task tests: stubs, probes, value rules, helper compute
  • Configuration: every Lakeflow argument, command-line flag and sync strategy
  • How it works: the deploy and teardown lifecycle, xdist, timing
  • Limitations and FAQ: notebook output read from the rendered notebook, what probes cost, what stays in the workspace
  • API reference

Contributing and licence

Bug reports and pull requests are welcome. See CONTRIBUTING.md for the development setup, the test suites and how releases work, and SUPPORT.md for where to ask questions.

Apache-2.0. See LICENSE.

Metadata

Release files for pytest-databricks-lakeflow 0.11.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-databricks-lakeflow 0.11.0
File Size Uploaded Provenance
pytest_databricks_lakeflow-0.11.0.tar.gz 322.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-databricks-lakeflow 0.11.0
File Interpreter ABI Platform
pytest_databricks_lakeflow-0.11.0-py3-none-any.whl Python 3 none any Details

Total release size: 454.1 kB

Release files / pytest_databricks_lakeflow-0.11.0.tar.gz

Download URL pytest_databricks_lakeflow-0.11.0.tar.gz
Size 322.5 kB
Tags Source
SHA-256 checksum
How to use checksums
58c08897d0e418f0b11f13fa4ab2e65066c5b33115fac7b68ed4a1ae9c92a680
BLAKE2b-256 checksum
How to use checksums
19e81786709b58345295c098947936d24e5a5610bd7b0f36fa4b748147e62593
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Release files / pytest_databricks_lakeflow-0.11.0-py3-none-any.whl

Download URL pytest_databricks_lakeflow-0.11.0-py3-none-any.whl
Size 131.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8038ee1a14c5bb6562cdceabfbc395132dec4327567706e102500a50cc404c2a
BLAKE2b-256 checksum
How to use checksums
80f2d6193ed748835e1b2cc015dfe1916d8423fea12992afca294252f531198a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

Security for pytest-databricks-lakeflow 0.11.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pytest_databricks_lakeflow-0.11.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page