在测试中创建和导入辅助函数,而不使用 py.test 在测试目录中创建包
Posted
技术标签:
【中文标题】在测试中创建和导入辅助函数,而不使用 py.test 在测试目录中创建包【英文标题】:Create and import helper functions in tests without creating packages in test directory using py.test 【发布时间】:2016-02-04 03:11:54 【问题描述】:问题
如何在测试文件中导入辅助函数而不在test
目录中创建包?
上下文
我想创建一个可以在多个测试中导入的测试辅助函数。说,像这样:
# In common_file.py
def assert_a_general_property_between(x, y):
# test a specific relationship between x and y
assert ...
# In test/my_test.py
def test_something_with(x):
some_value = some_function_of_(x)
assert_a_general_property_between(x, some_value)
使用 Python 3.5,搭配 py.test 2.8.2
当前的“解决方案”
我目前正在通过在我的项目的 test
目录(现在是一个包)中导入一个模块来做到这一点,但如果可能的话,我想用其他机制来做到这一点(这样我的 test
目录没有包,只有测试,测试可以在包的安装版本上运行,推荐here in the py.test documentation on good practices)。
【问题讨论】:
pytest 不鼓励__init__.py
-files 似乎很疯狂,但同时除了在测试之间共享帮助函数之外别无选择。我的头发因此变白了。
【参考方案1】:
您可以在 conftest.py 中定义一个辅助类,然后创建一个夹具来返回该类(或它的一个实例,具体取决于您的需要)。
import pytest
class Helpers:
@staticmethod
def help_me():
return "no"
@pytest.fixture
def helpers():
return Helpers
然后在你的测试中,你可以使用fixture:
def test_with_help(helpers):
helpers.help_me()
【讨论】:
虽然这种模式感觉有点 hacky,但这确实是 pytest 的错 IMO(为什么不为如此明显的要求提供一种机制?)我发现它比操纵导入路径更可取(我尽量避免尽可能一般地)作为接受的答案。 这种技术是否允许它们用于特殊的 pytest 回调,例如pytest_collection_modifyitems
?
@jxramos 这种方法使辅助对象可用作普通的 pytest 夹具。您可以使用pytest.mark.usefixtures
在收集阶段按名称将夹具动态添加到测试项。【参考方案2】:
我的选择是在tests
目录中创建一个额外的目录,并将其添加到 conftest 中的 pythonpath 中。
tests/
helpers/
utils.py
...
conftest.py
setup.cfg
在conftest.py
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), 'helpers'))
在setup.cfg
[pytest]
norecursedirs=tests/helpers
此模块将与import utils
一起使用,请注意名称冲突。
【讨论】:
我真的很喜欢这个解决方案,特别是因为它保留了代码的导入路径配置和测试',对我来说,这使得设计更简单。谢谢! 你能添加一个关于如何在任何 test_x.py 中导入 utils.py 中的类的 sn-p 吗? @sax,与仅使用def helper_fun():
在 test_module.py
中定义帮助函数相比,使用它有什么好处。只要辅助函数不以test_helper_fun
开头,pytest 就不会收集它。但是如果你想在特定的测试函数中运行它,你仍然可以调用helper_func
。是否有理由将其复杂化?
如果您确实想将其保存在单独的文件夹中,并且不想将辅助函数与 test_module.py
混合使用,您不能只使用 import from tests.helper.util import helper_fun
来访问辅助函数在 test_function 中?
建议使用 pytest 不要有tests
目录可导入,所以你不能从其他模块使用你的功能【参考方案3】:
在寻找这个问题的解决方案时,我遇到了这个 SO question,并最终采用了相同的方法。创建一个帮助程序包,使用 sys.path
使其可导入,然后只导入它...
这似乎不是最好的方法,所以我创建了pytest-helpers-namespace。这个插件允许你在 conftest.py
上注册帮助函数:
import pytest
pytest_plugins = ['helpers_namespace']
@pytest.helpers.register
def my_custom_assert_helper(blah):
assert blah
# One can even specify a custom name for the helper
@pytest.helpers.register(name='assertme')
def my_custom_assert_helper_2(blah):
assert blah
# And even namespace helpers
@pytest.helpers.asserts.register(name='me')
def my_custom_assert_helper_3(blah):
assert blah
然后,在测试用例函数体中就像使用它一样
def test_this():
assert pytest.helpers.my_custom_assert_helper(blah)
def test_this_2():
assert pytest.helpers.assertme(blah)
def test_this_3():
assert pytest.helpers.asserts.me(blah)
它非常简单,文档非常小。看看并告诉我它是否也解决了您的问题。
【讨论】:
酷,我去看看。谢谢! 我试过这个,但是我总是收到“RuntimeError: The helper was not registred”错误。 能否请您针对插件提交一张票,并附上如何触发 RuntimeError 的示例。 github.com/saltstack/pytest-helpers-namespace @s0undt3ch 感谢制作这个插件。在帮助下,我似乎无法使用 conftest.py 中预定义的固定装置。我如何将夹具与辅助功能一起使用? Ken,您能否针对插件仓库提出问题?自从 pytest 放弃了命名空间支持以来,该插件依赖于 hack 来工作。也许这就是问题所在,也许不是。【参考方案4】:要在不创建包的情况下从不同模块访问方法,并让该函数作为辅助函数运行,我发现以下方法很有帮助:
conftest.py:
@pytest.fixture
def compare_test_vs_actual():
def a_function(test, actual):
print(test, actual)
return a_function
test_file.py:
def test_service_command_add(compare_test_vs_actual):
compare_test_vs_actual("hello", "world")
【讨论】:
这也是一种创建动态装置的方法! 或def _compare_test_vs_actual(): pass
然后@fixture;def compare_test_vs_actual():return _compare_test_vs_actual
。嵌套更少,更清晰,尽管如果你想在你的灯具中接收一个灯具,上面可能更干净。【参考方案5】:
在测试文件夹中创建一个帮助程序包:
tests/
helpers/
__init__.py
utils.py
...
# make sure no __init__.py in here!
setup.cfg
在 setup.cfg 中:
[pytest]
norecursedirs=tests/helpers
可以通过import helpers
获得帮助程序。
【讨论】:
这对我来说是最干净的解决方案,而且工作完美。这应该是imo的参考方式。我很高兴在此处查看答案时一直向下滚动! 建议使用pytest.ini
作为官方文档推荐的配置文件。【参考方案6】:
你们中的一些人可能会完全接受我的建议。但是,使用来自其他模块的公共函数或值的非常简单的方法是将其直接注入公共工作区。示例:conftest.py:
import sys
def my_function():
return 'my_function() called'
sys.modules['pytest'].common_funct = my_function
test_me.py
import pytest
def test_A():
print(pytest.common_funct())
【讨论】:
这种方法与将其定义为夹具有什么区别? 这种方法允许您在测试函数之外使用辅助函数。例如在定义 pytest.parametrization 参数时【参考方案7】:作为另一种选择,这个目录结构对我有用:
mypkg/
...
test_helpers/
__init__.py
utils.py
...
tests/
my_test.py
...
然后在my_test.py
中使用以下命令导入实用程序:
from test_helpers import utils
【讨论】:
我相信这与我在问题中描述的情况相同,代码和测试没有明确分开。例如,对于发布等,您还必须考虑排除test_helpers
。以上是关于在测试中创建和导入辅助函数,而不使用 py.test 在测试目录中创建包的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Protractor 中创建和操作 Promise?
在 Android 测试中创建和使用 gradle 或系统属性
在 R 中的函数中创建和使用新变量:tidyverse 中的 NSE 编程错误