模拟pty/termios的windows测试



我正在开发一个应用程序,它本应在linux上运行,并依赖于ptytermios模块(我认为这并不重要,但我正在Airflow上编写一些DAG(。我使用windows工作站。我想在本地机器上运行单元测试,并且我正在尝试使用unittest.mock.patch来防止导入(在我的操作系统上不存在(模块。

尽管如此,我还是有问题,我不知道如何设置导入以避免错误。

一个最小的例子是

lib.py

import termios
class C: ...

foo.py

from lib import C
def bar(): ...

test_foo.py

import foo
def test_foo_bar():
assert foo.bar() == ...

什么";魔术;我应该在test_foo中写入以避免错误吗

>pytest test_foo.py
============================================================================================ test session starts =============================================================================================
platform win32 -- Python 3.7.9, pytest-7.0.1, pluggy-1.0.0
rootdir: C:Usersvito.detullioDesktopworkspace-srm-cognitivebe-airflow-dags, configfile: pytest.ini
plugins: anyio-3.4.0, cov-3.0.0
collected 0 items / 1 error                                                                                                                                                                                   
=================================================================================================== ERRORS ===================================================================================================
_____________________________________________________________________________________ ERROR collecting delme/test_foo.py _____________________________________________________________________________________
ImportError while importing test module 'C:Usersvito.detullioDesktopworkspace-srm-cognitivebe-airflow-dagsdelmetest_foo.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
c:Program FilesPython37libimportlib__init__.py:127: in import_module
return _bootstrap._gcd_import(name[level:], package, level)
test_foo.py:1: in <module>
import foo
foo.py:1: in <module>
from lib import C
lib.py:1: in <module>
import termios
E   ModuleNotFoundError: No module named 'termios'
========================================================================================== short test summary info ===========================================================================================
ERROR test_foo.py
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
============================================================================================== 1 error in 0.14s ==============================================================================================

您首先必须确保不会通过顶级导入导入不存在的模块。如果仅在本地导入,则可以在进行导入之前将一些占位符模块添加到模块缓存中:

import sys
from unittest import mock

def test_foo_bar():
if "termios" not in sys.modules:
sys.modules["termios"] = mock.MagicMock()
import foo
assert foo.bar() == ...

当然,这是假设您在测试中实际上并不需要该模块。

请注意,您也不能通过patch装饰器来修补bar或该模块中的其他内容,因为这也会在您有机会修补模块之前导入该模块,即使它没有直接引用它。您必须在测试内部进行修补。

我最近也遇到了同样的气流问题。受到这个答案的启发,我决定嘲笑所有与气流相关的模块。其思想是使用Python标准库中包含的sys模块中的sys.modules字典来编辑模块映射。例如,如果您只需要模拟decorators.airflow模块中的task(这在Windows上不起作用(,则可以使用以下代码:

airflow_module = type(sys)("airflow")
airflow_module.decorators = type(sys)("decorators")
airflow_module.decorators.task = MagicMock()
sys.modules["airflow"] = airflow_module
sys.modules["airflow.decorators"] = airflow_module.decorators

您可以使用此方法模拟每个airflow子模块(也适用于子模块(。例如,在我的案例中,我向conftest.py添加了以下代码,以模拟来自不同airflow子模块的taskget_current_contextVariableTriggerRule

if platform.system() == 'Windows':
airflow_module = type(sys)("airflow")
airflow_module.decorators = type(sys)("decorators")
airflow_module.operators = type(sys)("operators")
airflow_module.operators.python = type(sys)("python")
airflow_module.models = type(sys)("models")
airflow_module.models.variable = type(sys)("variable")
airflow_module.utils = type(sys)("utils")
airflow_module.utils.trigger_rule = type(sys)("trigger_rule")
airflow_module.decorators.task = MagicMock()
airflow_module.operators.python.get_current_context = MagicMock()
airflow_module.models.variable.Variable = MagicMock()
airflow_module.utils.trigger_rule.TriggerRule = MagicMock()
sys.modules["airflow"] = airflow_module
sys.modules["airflow.decorators"] = airflow_module.decorators
sys.modules["airflow.operators"] = airflow_module.operators
sys.modules["airflow.operators.python"] = airflow_module.operators.python
sys.modules["airflow.models"] = airflow_module.models
sys.modules["airflow.models.variable"] = airflow_module.models.variable
sys.modules["airflow.utils"] = airflow_module.utils
sys.modules["airflow.utils.trigger_rule"] = airflow_module.utils.trigger_rule

最新更新