pytest-airflow-in-a-box
Your Dag files import. Your task callables pass. Production still breaks.
Did you verify the
DAG?
Import and callable tests do not exercise trigger rules, branch skips, rendered templates,
connection resolution, or operator serialization. pytest-airflow-in-a-box tests those seams
in pytest, before deployment -- no scheduler, webserver, or live Airflow environment required.
For teams that own Airflow behavior: Dags in a repository, custom operators, hooks, sensors,
connection types, live REST API integrations, or an Airflow 2-to-3 migration. If your repo is
100% stock operators, dag.test() plus a DagBag import test is enough; the full list is on the
documentation site.
Already have a DagBag import test and a pile of task.function(...) calls? Here is
exactly where they stop,
and why not dag.test(), DebugExecutor, or your own conftest.py.
Quickstart
def test_my_dag(dag_bag, run_dag):
dag = dag_bag.dags["my_dag_id"]
result = run_dag(dag)
assert result.success
assert result.order == ["extract", "load"]
pytest --dag-folder=dags
run_dag proves your real file, under its real dag_id, actually finishes in the states
you expect. dag_bag parses the folder once per worker process, and result.order records
execution order rather than graph topology. In-test Dags (dag_maker), single operators
without a database (run_task), and matchers are in the
Quickstart.
Installation
uv add --dev "pytest-airflow-in-a-box[airflow3]"
The plugin does not depend on Airflow directly: the Airflow 2.x monolith and the 3.x core both
install under the name apache-airflow, so a hard plugin pin would corrupt whichever family you did not
choose. The airflow3 extra pins apache-airflow>=3.1,<4, matching the dependency restriction
in pyproject.toml. Projects that already pin Airflow themselves -- for example through
Airflow's published constraints files -- install the plugin bare. Every extra and common
combination is listed under
Dependencies and extras.
GitHub Action
nredd/pytest-airflow-in-a-box/action@v0 provisions a constraints-pinned Airflow environment;
your workflow still runs pytest.
name: Airflow tests
on: [pull_request]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: nredd/pytest-airflow-in-a-box/action@v0
id: airflow-env
with:
airflow-version: "3.3.1"
python-version: "3.13"
- run: ${{ steps.airflow-env.outputs.python-path }} -m pytest
The action does not run tests, cache packages, upload artifacts, or start Docker. See GitHub Actions and reports for every input and output, report uploads, version matrices, and pinning.
Requirements
- CPython 3.10 through 3.14, pytest 8 or newer
- Apache Airflow 3.1 or newer, below 4; or a certified Airflow 2 release from 2.7 through 2.11
- Linux or macOS. Airflow has no native Windows support -- use WSL2 or the devcontainer
Which Airflow and Python combinations are actually exercised in CI, and what the 2.x tier does
and does not cover, are stated once in
Compatibility and certification.
After installing, pytest --airflow-doctor tells you whether your own pin works.
What ships
Typed fixtures, grouped by the job:
| Job | Reach for |
|---|---|
| Run one operator with no database | run_task, render_task, task_context |
Run a real DagRun and assert on it |
dag_maker, run_dag, dag_bag |
| Give the run its environment | airflow_home, airflow_configure, airflow_variables, airflow_connections |
| Register a custom component for one test | airflow_components |
| Assert on what a task logged | cap_structlog |
| Check every Dag at once | dag_corpus |
| Talk to a live Airflow API | api_client, api_base_url |
Every fixture, its return type, and its scope: Fixtures. Markers are listed in Markers.
Also in the box: corpus smoke checks (--airflow-smoke), a disposable metadata database, an
isolated AIRFLOW_HOME, report artifacts that survive -n auto, a
GitHub Action, and an
Airflow 2-to-3 migration toolkit fronted by the airflow-migration-diff console script.
Documentation
The documentation site follows the reader, one deep link per stage:
- Whose fail is it anyway? -- what earns a test
- The fidelity ladder -- which rung to stand on, and what each one costs
- Smoke Tests -- properties of the whole corpus
- Checking components -- validate Airflow extension points before registration
- Airflow 2->3 Migration -- arrive migrating, leave with a suite
- Under the hood -- what
_compat/absorbs, and why
Contributing, the local gate, and running CI with act:
CONTRIBUTING.md and
Developing this plugin.
License
Apache License 2.0. See LICENSE, NOTICE, and PROVENANCE.md.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytest_airflow_in_a_box-0.13.1.tar.gz.
File metadata
- Download URL: pytest_airflow_in_a_box-0.13.1.tar.gz
- Upload date:
- Size: 359.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d80e1f64d040e4f8d902e356c845f57f80195652cc4ec3019ac502595e37d86
|
|
| MD5 |
604a4f986d119b4642ad17c5d5cae23e
|
|
| BLAKE2b-256 |
955406ec43a5b28055be76e211d32d90944a020bae431ec2efa5f3e355348967
|
Provenance
The following attestation bundles were made for pytest_airflow_in_a_box-0.13.1.tar.gz:
Publisher:
release.yml on nredd/pytest-airflow-in-a-box
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_airflow_in_a_box-0.13.1.tar.gz -
Subject digest:
7d80e1f64d040e4f8d902e356c845f57f80195652cc4ec3019ac502595e37d86 - Sigstore transparency entry: 2657733816
- Sigstore integration time:
-
Permalink:
nredd/pytest-airflow-in-a-box@85e221f0be87c01356661ef6da4e52394e020ad9 -
Branch / Tag:
refs/tags/v0.13.1 - Owner: https://github.com/nredd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@85e221f0be87c01356661ef6da4e52394e020ad9 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pytest_airflow_in_a_box-0.13.1-py3-none-any.whl.
File metadata
- Download URL: pytest_airflow_in_a_box-0.13.1-py3-none-any.whl
- Upload date:
- Size: 409.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7cae7b019dac199b3f4396ab984935a1d4a4438d02fca0b36b3eae5b9440987a
|
|
| MD5 |
fb11273d73b0ebc9de6157d1dfdff7f7
|
|
| BLAKE2b-256 |
2355e3519455b435bdd0c77c0bbade6cbc86bd0a1d86fbf47d0ef1ff31ab1168
|
Provenance
The following attestation bundles were made for pytest_airflow_in_a_box-0.13.1-py3-none-any.whl:
Publisher:
release.yml on nredd/pytest-airflow-in-a-box
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_airflow_in_a_box-0.13.1-py3-none-any.whl -
Subject digest:
7cae7b019dac199b3f4396ab984935a1d4a4438d02fca0b36b3eae5b9440987a - Sigstore transparency entry: 2657733883
- Sigstore integration time:
-
Permalink:
nredd/pytest-airflow-in-a-box@85e221f0be87c01356661ef6da4e52394e020ad9 -
Branch / Tag:
refs/tags/v0.13.1 - Owner: https://github.com/nredd
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@85e221f0be87c01356661ef6da4e52394e020ad9 -
Trigger Event:
release
-
Statement type: