【问题标题】:Test Dag run for Airflow 1.9 in unittest在单元测试中为 Airflow 1.9 运行测试 Dag
【发布时间】:2018-12-05 10:06:03
【问题描述】:

我已经实现了运行单个 dag 的测试用例,但它似乎在 1.9 中不起作用,可能是由于气流 1.8 中引入了更严格的池 . 我正在尝试在测试用例下运行:

from airflow import DAG
from airflow.operators.dummy_operator import DummyOperator
from airflow.operators.python_operator import PythonOperator

class DAGTest(unittest.TestCase):
    def make_tasks(self):
        dag = DAG('test_dag', description='a test',
                  schedule_interval='@once',
                  start_date=datetime(2018, 6, 26),
                  catchup=False)


        du1 = DummyOperator(task_id='dummy1', dag=dag)
        du2 = DummyOperator(task_id='dummy2', dag=dag)
        du3 = DummyOperator(task_id='dummy3', dag=dag)
        du1 >> du2 >> du3
        dag.run()

    def test_execute(self):
        self.make_tasks() 

例外:

Dependencies not met for <TaskInstance: test_dag.dummy3 2018-06-26 00:00:00  [upstream_failed]>, dependency 'Trigger Rule' FAILED: Task's trigger rule 'all_success' requires all
    upstream tasks to have succeeded, but found 1 non-success(es).
    upstream_tasks_state={'skipped': 0L, 'successes': 0L, 'failed': 0L,'upstream_failed': 1L, 'done': 1L, 'total': 1}, upstream_task_ids=['dummy2']

我做错了什么? 我已经尝试过 LocalExecutor 和 SequentialExecutor

环境:

Python 2.7
Airflow 1.9

我相信它正在尝试同时执行所有任务而不尊重依赖关系。 注意:类似代码用于 Airflow 1.7

【问题讨论】:

  • 您没有包含足够的实际错误消息:airflow.exceptions.AirflowException: dag_id could not be found: test_dag. Either the dag did not exist or it failed to parse.
  • 我首先在堆栈跟踪中看到上述异常,然后 dag_id 未找到异常。所以不确定是哪一个导致了这个问题。

标签: airflow


【解决方案1】:

这是一个您可以在 pytest 测试用例中使用的函数,它将按顺序运行 DAG 的任务。

from datetime import timedelta
import pytest
from unittest import TestCase


@pytest.fixture
def test_dag(dag):
    dag._schedule_interval = timedelta(days=1)  # override cuz @once gets skipped
    done = set([])

    def run(key):
        task = dag.task_dict[key]
        for k in task._upstream_task_ids:
            run(k)
        if key not in done:
            print(f'running task {key}...')
            date = dag.default_args['start_date']
            task.run(date, date, ignore_ti_state=True)
            done.add(key)
    for k, _ in dag.task_dict.items():
        run(k)

然后您可以在测试中使用 test_dag(dag) 而不是 dag.run()

您需要确保您在自定义运算符中的登录使用self.log.info() 而不是logging.info()print(),否则它们不会显示。

您可能还需要使用 python -m pytest -s test_my_dag.py 运行测试,因为没有 -s 标志将不会捕获 Airflow 的标准输出。

我仍在尝试弄清楚如何处理 DAG 间的依赖关系。

【讨论】:

  • 感谢第谷的回答。很好的解释,但我需要运行具有任务依赖关系的 dag 以测试整个 dag,因为我希望看到一些上游失败,然后下一个任务报告它。我已经找到了一种将 dag 保存在文件中并在运行期间将其加载到 dagbag 中的方法,但是我仍然想动态创建一个具有依赖关系的 dag 并对其进行测试。如果我找到任何解决方案,将继续发布。再次感谢
  • @mad_ 你的意思是 DAG 中任务之间的依赖关系?因为已经可以了。仍然是 WIP 的依赖项是传感器。
  • 是的,我得到了您的解决方案,您正在尝试解决 dag 中的个别任务。你如何创建一个 dag?我可能会试一试。对我来说似乎很有希望
  • 任何 dag 都可以!您可以像在示例中那样内联定义它,也可以像 from my_dag import dag 那样在测试中导入它(即使使用 with DAG(...) as dag: 的构造,它也会导出)。
  • 我尝试了您的解决方案。似乎它正在工作,但没有我试图访问的 xcom。所以没有行被推入 xcom。不知道为什么会发生这种情况,因为它们是单独运行的。
【解决方案2】:

我不熟悉 Airflow 1.7,但我猜它没有 Airflow1.8 及更高版本所具有的“DagBag”概念。

你不能运行你这样创建的 DAG,因为dag.run() 启动了一个新的 python 进程,它必须从它在磁盘上解析的 dag 文件夹中找到 DAG - 但它不能。这作为消息包含在输出中(但您没有包含完整的错误消息/输出)

你想通过在测试文件中创建一个 dag 来测试什么?它是自定义运算符吗?那么你最好直接测试它。例如,这是我如何独立测试自定义运算符:

class MyPluginTest(unittest.TestCase)
    def setUp(self):
        dag = DAG(TEST_DAG_ID, schedule_interval='* * * * Thu', default_args={'start_date': DEFAULT_DATE})
        self.dag = dag
        self.op = myplugin.FindTriggerFileForExecutionPeriod(
            dag=dag,
            task_id='test',
            prefix='s3://bucket/some/prefix',
        )
        self.ti = TaskInstance(task=self.op, execution_date=DEFAULT_DATE)

        # Other S3 setup here, specific to my test


    def test_execute_no_trigger(self):
        with self.assertRaises(RuntimeError):
            self.ti.run(ignore_ti_state=True)

        # It shouldn't have anything in XCom
        self.assertEqual(
            self.ti.xcom_pull(task_ids=self.op.task_id),
            None
        )

【讨论】:

  • 我必须测试具有依赖关系的自定义运算符,如上例所示。您是建议单独运行 dag 还是我遗漏了什么?我可以以某种方式在 DagBag 中添加 dag 吗?
  • 你有自定义的deps,还是只有自定义的操作符?我会单独测试操作员。您不能将其添加到 dag 袋中,因为每个新过程都会将它们加载到新鲜的,因此将它们添加到 dag 袋中不是一种选择。如果我展示的测试操作符的示例对您不起作用,那么唯一的其他选择就是在磁盘上运行一个测试 dag,然后使用它。可能通过将AIRFLOW__CORE__DAGS_FOLDER 设置为指向您的测试目录。
  • 以上代码只测试一项任务,而不是整个 dag。对于另一种方法,我应该先腌制 dag 使其可见,然后将 Dag 目录更改为测试目录吗?
  • 谢谢 我花了一段时间才弄清楚如何实施。我还是想知道为什么 Airflow 不支持完整的 dag 测试
  • 许多操作符需要在命令行中通过 conf 传递参数。除非您处于 dag run 的上下文中,否则这些将不可用。例如,如果您的示例尝试访问命令行参数(或通过 rest 传递的参数),它将引发异常。如何在不运行 dag 的情况下测试此类运算符以生成上下文?
猜你喜欢
  • 2012-11-28
  • 2011-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-29
  • 2016-11-18
相关资源
最近更新 更多