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.
Metadata
Release files for pytest-airflow-in-a-box 0.13.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pytest_airflow_in_a_box-0.13.1.tar.gz | 359.0 kB | With provenance | Details for pytest_airflow_in_a_box-0.13.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pytest_airflow_in_a_box-0.13.1-py3-none-any.whl | Python 3 | none | any | Details for pytest_airflow_in_a_box-0.13.1-py3-none-any.whl |
Total release size: 768.6 kB
Release files / pytest_airflow_in_a_box-0.13.1.tar.gz
| Download URL | pytest_airflow_in_a_box-0.13.1.tar.gz |
|---|---|
| Size | 359.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7d80e1f64d040e4f8d902e356c845f57f80195652cc4ec3019ac502595e37d86
|
|
BLAKE2b-256 checksum How to use checksums |
955406ec43a5b28055be76e211d32d90944a020bae431ec2efa5f3e355348967
|
| 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 Aug 31, 2026.
Transparency logRelease files / pytest_airflow_in_a_box-0.13.1-py3-none-any.whl
| Download URL | pytest_airflow_in_a_box-0.13.1-py3-none-any.whl |
|---|---|
| Size | 409.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7cae7b019dac199b3f4396ab984935a1d4a4438d02fca0b36b3eae5b9440987a
|
|
BLAKE2b-256 checksum How to use checksums |
2355e3519455b435bdd0c77c0bbade6cbc86bd0a1d86fbf47d0ef1ff31ab1168
|
| 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 Aug 31, 2026.
Transparency logSecurity for pytest-airflow-in-a-box 0.13.1
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.
Attestation for pytest_airflow_in_a_box-0.13.1.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 Aug 31, 2026.
Transparency logA 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.
To learn more about protecting yourself from malicious actors, visit our package security documentation.