在测试中创建和导入辅助函数,而不使用 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 或系统属性

在 C 中创建和显示链表:程序显示不正确

在 R 中的函数中创建和使用新变量:tidyverse 中的 NSE 编程错误

如何在 Visual Studio 代码中创建和部署函数应用程序?

如何在 MATLAB 中创建和保存大型数据集?