{ "info": { "author": "Guilherme Zagatti", "author_email": "guilherme.zagatti@flowminder.org", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Framework :: Pytest", "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", "Natural Language :: English", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7" ], "description": "pytest-airflow: pytest support for airflow\n==========================================\n\n.. image:: https://circleci.com/gh/Flowminder/pytest-airflow.svg?style=svg&circle-token=7e32dee2ea47f7961e93b9016d44bda103b3bede\n :target: https://circleci.com/gh/Flowminder/pytest-airflow\n\n``pytest-airflow`` is a plugin for ``pytest`` that allows tests to be run\nwithin an Airflow DAG.\n\n``pytest`` handles test discovery and function encapsulation, allowing\ntest declaration to operate in the usual way with the use of\nparametrization, fixtures and marks. The generated test callables tests\nare eventually passed to ``PythonOperators`` that are run as separate\nAirflow tasks.\n\nInstallation\n------------\n\n``pytest-airflow`` can be installed with ``pip``:\n\n.. code-block:: bash\n\n pip install pytest-airflow\n\nNote\n~~~~\n\n``pytest-airflow`` depends on Apache Airflow, which requires\n``export SLUGIFY_USES_TEXT_UNIDECODE=yes`` to be specified before install. See\nthe `Airflow install instructions `_\nfor background on this requirement.\n\nUsage\n-----\n\nWhen running pytest from the command line, the plugin will collect the\ntests and construct the DAG. It will output a DAG tree view in addition to\nthe requested output.\n\n.. code-block:: bash\n\n $ pytest --airflow\n\nWhen invoking pytest from python code, ``pytest.main()`` will\nreturn a reference to the DAG.\n\n.. code-block:: python\n\n import pytest\n dag, source, sink = pytest.main([\"--airflow\", \"--dag-id\", \"FOO\"])\n\nThe plugin generates two tasks at the start and end of the workflow which\nrepresent the source and sink for the tests. The source task is\nresponsible for branching and the sink task for reporting. The former and\nthe later are called ``__pytest_source`` and ``__pytest_sink`` by default\nrespectively. In case the user desire to change those defaults name it is \npossible to make use of the ``source`` and ``sink`` flags as below.\n\n.. code-block:: bash\n\n $ pytest --airflow --source branch --sink report\n\nIf the plugin is installed, ``pytest`` will automatically use it. Saving\nthe script above in one's DAG folder is enough to trigger the DAG. Note\nthat ``pytest`` will be evaluated from the path where the Airflow\nscheduler is invoked.\n\nPlugin\n------\n\nThe plugin creates a DAG of the form ``source -> tests -> sink``,\n``source`` marks tests that will be executed and skipped, ``tests``\nexecutes the selected tests as separate tasks and ``sink`` reports test\noutcome.\n\nBranching\n~~~~~~~~~\n\nAirflow requires that any DAG be completely defined before it is run. So\nby the nature of Airflow, we cannot use ``pytest`` to collect tests on the\nfly based on the results of ``source``. Rather, ``pytest`` is used to\ngenerate the set of all possible desired tests before ``source`` is\nevaluated. The user can use all of the available flags to ``pytest`` (eg.\n``-m``, ``-k``, paths) to narrow the set of initial desired tests down.\n\nThe plugin makes a source task called ``__pytest_source`` by default\navailable. This task allows skipping unwanted tests for a particular DAG\nrun using the following configuration keys:\n\n* ``marks``: a list of marks, it filters tests in the same way as the\n ``-m`` flag operates when collecting tests with ``pytest``.\n\n* ``keywords``: a list of keywords, it filters tests in the same way as\n the ``-k`` flag operates when collecting tests with ``pytest``.\n\nFixtures\n~~~~~~~~\n\nThe plugin defers test execution for the DAG run. That means when calling\n``pytest``, the tests will be collected and the associated callables will\nbe generated and passed to the ``PythonOperator``. If the DAG is compiled\nwithout any errors, ``pytest`` will return the DAG and will exit\nsucessfully. That means that it will report that all tests passed, which\nonly means that the DAG was compiled without any problems.\n\nFixture setup and teardown are executed at the moment of DAG compilation.\nThat means that fixtures such as database connections will not be\navailable at the moment of test execution during a DAG run.\n\nIn order to get around this problem there are two alternatives. The first\nalternative is to implement a fixture as a factory, and handling fixture\nteardown on the test itself.\n\nAlternatively, the plugin allows deferred fixture setup and teardown. In\norder to achieve deferred execution, the name of the fixture must be\nprefixed with ``defer_`` or it must depend on the reserved fixture\n``task_ctx``. That means that the plugin defer the execution of such\nfixtures until the DAG is run. Fixtures that depend on a deferred fixture\nwill also have its execution deferred for later.\n\nThe reserved fixture ``task_ctx`` is always deferred. This fixture\nevaluates the Airflow task context and is available to the user when\nwritting tests. Using this fixture, the user has access to all the items\nthat would be available to ``kwargs`` when setting ``provide_context`` to\n``True`` when using the ``PythonOperator`` in Airflow.\n\nAll in all, collection time fixture execution should be used for test\nparametrization, for generating expensive resources that can be made\navailable to tests as copies and for generating fixture factories. On the\nother hand, deferred fixtures are great for database connections and other\nresources that need to be recycled at each test execution.\n\nReporting\n~~~~~~~~~\n\nFinally, the sink task ``report`` can be used for reporting purposes and for\ncommunicating test results to other DAGs using the ``xcom`` channel. The user\ncan supply its own ``dag_report`` fixture for customizing its reporting\nrequirements. The plugin expects the following fixture signature, scoped at the\n``session`` level.\n\n.. code-block:: python\n\n @pytest.fixture(scope=\"session\")\n def dag_report(**kwargs):\n ...\n\n\nDAG Configuration\n~~~~~~~~~~~~~~~~~\n\nThe user can configure the DAG using two reserved fixtures for this. The\nfixtures must be scoped at the ``session`` level and its location should cover\nall the collected test items. The most narrow fixture that covers all of the\ncollected items will be selected. Otherwise, the plugin uses default values for\nthose fixtures. Apart from that, fixture execution and discovery should operate\nin the usual way.\n\nThe first fixture is ``dag_default_args``, which should return\na dictionary with ``default_args`` that will be passed to the dag\ninitialization. The default returns\n\n.. code-block:: python\n\n { \"owner\": \"airflow\",\n \"start_date\": datetime.datetime(2018, 1, 1),\n \"end_date\": None,\n \"depends_on_past\": False,\n }\n\nThe second fixture is ``dag`` which should return an Airflow DAG that will\nbe used throughout the script.\n\nIf the user desires only to modify the name of the DAG, it is possible to\nsimply pass the ``--dag-id`` flag to the ``pytest`` cmdline.\n\nIf the user desires to integrate the DAG generated from this plugin in\nher/his own DAG. One option is to define the whole DAG inside the same\n``conftest.py`` file that is used by ``pytest`` to initialize the tests.\nIf this is not possible and the DAG must be defined separately, it is\npossible to create a custom ``pytest`` plugin in the same file where the\nDAG is created and pass such plugin to ``pytest.main`` as the example\nbelow illustrates.\n\n.. code-block:: python\n\n import pytest\n from airflow import DAG\n\n my_dag = DAG(dag_id=\"foo\", start_date = \"2017-01-01\")\n\n class MyPlugin:\n\n @pytest.fixture(scope=\"session\")\n def dag(self):\n return my_dag\n\n my_dag, source, sink = pytest.main([\"--airflow\"], plugins=[MyPlugin()])\n\nLicense\n-------\n\nThis Source Code Form is subject to the terms of the Mozilla Public\nLicense, v. 2.0. If a copy of the MPL was not distributed with this\nfile, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/Flowminder/pytest-airflow", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "pytest-airflow", "package_url": "https://pypi.org/project/pytest-airflow/", "platform": "", "project_url": "https://pypi.org/project/pytest-airflow/", "project_urls": { "Homepage": "https://github.com/Flowminder/pytest-airflow" }, "release_url": "https://pypi.org/project/pytest-airflow/0.0.3/", "requires_dist": [ "pytest (>=4.4.0)", "apache-airflow (>=1.8.0)" ], "requires_python": ">=3.5", "summary": "pytest support for airflow.", "version": "0.0.3" }, "last_serial": 5091828, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "52a72c4a097b151c7866f0f4b064c5c1", "sha256": "d45121b2d37de6acec40e9580484699a28ba0f1f8196d9a349ea5238ddc1d9c6" }, "downloads": -1, "filename": "pytest_airflow-0.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "52a72c4a097b151c7866f0f4b064c5c1", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 18811, "upload_time": "2019-03-06T16:52:14", "url": "https://files.pythonhosted.org/packages/71/c7/295a0c5d63938c2ab732f862f79d5f6f0b66197e9312427481f4becf00a1/pytest_airflow-0.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "65a07dd2367f6a3c820d2b8f504ab750", "sha256": "ed0660824943739674eedd09c06f5932226aadb77404591005fc3419179621ec" }, "downloads": -1, "filename": "pytest-airflow-0.0.1.tar.gz", "has_sig": false, "md5_digest": "65a07dd2367f6a3c820d2b8f504ab750", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 35311, "upload_time": "2019-03-06T16:52:17", "url": "https://files.pythonhosted.org/packages/32/fa/12dbc87c6a42b09c9eed7f1ba4716bb573f85f5f0e27dff4efed4fbd1226/pytest-airflow-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "0c99dc63d734f1e6b2929ee862b84fce", "sha256": "9b46304008ffa8393ccfcd30300ed5198ca18a9bc859e04d9ccf5e30a589b71e" }, "downloads": -1, "filename": "pytest_airflow-0.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "0c99dc63d734f1e6b2929ee862b84fce", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 18967, "upload_time": "2019-03-08T09:05:18", "url": "https://files.pythonhosted.org/packages/96/5a/f88df8aa66b0595357cf63a049a67bad04d83a9892f0c726dae36ff0a235/pytest_airflow-0.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "14a3f1c9c5e78fc03c032bbbbdbc005d", "sha256": "f49f81c399028132aa89a614a220e03e6077b2cc3d8600a6f9bb17acde1542d0" }, "downloads": -1, "filename": "pytest-airflow-0.0.2.tar.gz", "has_sig": false, "md5_digest": "14a3f1c9c5e78fc03c032bbbbdbc005d", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 35832, "upload_time": "2019-03-08T09:05:19", "url": "https://files.pythonhosted.org/packages/b2/04/fdc8dabb5931b334ffac50a4325d9d57bb57454635f5ffe973055f78f21e/pytest-airflow-0.0.2.tar.gz" } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "e03ce1bae042cb2322cef60b957b8595", "sha256": "d6afbfe0847379d90080f578c6a2964bbef301d4b4e9929ec422858e809f0ec6" }, "downloads": -1, "filename": "pytest_airflow-0.0.3-py3-none-any.whl", "has_sig": false, "md5_digest": "e03ce1bae042cb2322cef60b957b8595", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 19141, "upload_time": "2019-04-03T16:16:41", "url": "https://files.pythonhosted.org/packages/56/8c/3d8d623e0c59f4f7aaf048f9f3da8b36b2c5d0ac5cd77129f0670db7c520/pytest_airflow-0.0.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1ee1a2c84fe50aa017a341d9db7cf4d8", "sha256": "5c660cd16c1c6f0e80e68478c45bf3e4c995194c8a1e3c42c1eb7f639c127d32" }, "downloads": -1, "filename": "pytest-airflow-0.0.3.tar.gz", "has_sig": false, "md5_digest": "1ee1a2c84fe50aa017a341d9db7cf4d8", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 35991, "upload_time": "2019-04-03T16:16:42", "url": "https://files.pythonhosted.org/packages/c7/60/97494d30bedb50b4eea5367a63cbadaac2aeb8034218e479d768a5746fd4/pytest-airflow-0.0.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "e03ce1bae042cb2322cef60b957b8595", "sha256": "d6afbfe0847379d90080f578c6a2964bbef301d4b4e9929ec422858e809f0ec6" }, "downloads": -1, "filename": "pytest_airflow-0.0.3-py3-none-any.whl", "has_sig": false, "md5_digest": "e03ce1bae042cb2322cef60b957b8595", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.5", "size": 19141, "upload_time": "2019-04-03T16:16:41", "url": "https://files.pythonhosted.org/packages/56/8c/3d8d623e0c59f4f7aaf048f9f3da8b36b2c5d0ac5cd77129f0670db7c520/pytest_airflow-0.0.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1ee1a2c84fe50aa017a341d9db7cf4d8", "sha256": "5c660cd16c1c6f0e80e68478c45bf3e4c995194c8a1e3c42c1eb7f639c127d32" }, "downloads": -1, "filename": "pytest-airflow-0.0.3.tar.gz", "has_sig": false, "md5_digest": "1ee1a2c84fe50aa017a341d9db7cf4d8", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.5", "size": 35991, "upload_time": "2019-04-03T16:16:42", "url": "https://files.pythonhosted.org/packages/c7/60/97494d30bedb50b4eea5367a63cbadaac2aeb8034218e479d768a5746fd4/pytest-airflow-0.0.3.tar.gz" } ] }