在测试中创建和导入辅助函数,而无需使用py.test在测试目录中创建包

Jua*_*oto 35 python unit-testing pytest

如何在不在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)
Run Code Online (Sandbox Code Playgroud)

使用Python 3.5,py.test 2.8.2


当前"解决方案"

我目前正在通过在我的项目test目录(现在是一个包)中导入一个模块来实现这一点,但是如果可能的话我想用其他一些机制来做(因为我的test目录没有包但只是测试,并且可以在已安装的软件包版本上运行测试,如py.test文档中关于良好实践的建议).

sax*_*sax 26

我的选择是在testsdir中创建一个额外的目录并将其添加到conftest中的pythonpath中.

tests/
    helpers/
      utils.py
      ...
    conftest.py
setup.cfg
Run Code Online (Sandbox Code Playgroud)

在里面 conftest.py

import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), 'helpers'))
Run Code Online (Sandbox Code Playgroud)

setup.cfg

[pytest]
norecursedirs=tests/helpers
Run Code Online (Sandbox Code Playgroud)

这个模块将可用import utils,只需要小心命名冲突.

  • 我真的很喜欢这个解决方案,特别是因为它保持代码的导入路径配置与测试',这对我来说,使设计更简单.谢谢! (2认同)

aug*_*rar 17

你可以在conftest.py中定义一个帮助器类,然后创建一个返回该类的fixture(或者它的一个实例,具体取决于你需要的).

import pytest


class Helpers:
    @staticmethod
    def help_me():
        return "no"


@pytest.fixture
def helpers():
    return Helpers
Run Code Online (Sandbox Code Playgroud)

然后在测试中,您可以使用夹具:

def test_with_help(helpers):
    helpers.help_me()
Run Code Online (Sandbox Code Playgroud)

  • 虽然这种模式感觉有点 hacky,但在我看来,这确实是 pytest 的错误(为什么不为如此明显的要求提供一种机制?),而且我发现它比操纵导入路径(我通常会尽可能避免)更好接受的答案确实如此。 (6认同)

s0u*_*3ch 15

在寻找这个问题的解决方案时,我遇到了这个问题,并最终采用了相同的方法.创建一个帮助程序包,重新设置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
Run Code Online (Sandbox Code Playgroud)

然后,在一个测试用例中,函数体就像使用它一样

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)
Run Code Online (Sandbox Code Playgroud)

它非常简单,文档很小.看一看,告诉我它是否也解决了你的问题.


小智 11

在测试文件夹中创建一个助手包:

tests/
    helpers/
      __init__.py
      utils.py
      ...
    # make sure no __init__.py in here!
setup.cfg
Run Code Online (Sandbox Code Playgroud)

在 setup.cfg 中:

[pytest]
norecursedirs=tests/helpers
Run Code Online (Sandbox Code Playgroud)

助手将可用import helpers

  • 建议按照官方文档的建议使用“pytest.ini”作为配置文件。 (4认同)
  • 或者`pyproject.toml` (3认同)
  • 对我来说,这感觉是最干净的解决方案,并且工作完美。在我看来,这应该是*参考方式。我很高兴在查看这里的答案时一直向下滚动! (2认同)

小智 7

要从不同模块访问方法而不创建包,并让该函数作为辅助函数运行,我发现以下帮助:

conftest.py:

@pytest.fixture
def compare_test_vs_actual():
    def a_function(test, actual):
        print(test, actual)
    return a_function
Run Code Online (Sandbox Code Playgroud)

test_file.py:

def test_service_command_add(compare_test_vs_actual):
    compare_test_vs_actual("hello", "world")
Run Code Online (Sandbox Code Playgroud)

  • 或者`def _compare_test_vs_actual(): pass`然后`@fixture;def compare_test_vs_actual():return _compare_test_vs_actual`。更清晰,嵌套更少,但如果您想在固定装置中接收固定装置,以上可能更清洁。 (2认同)

Uri*_*Uri 7

你们中的一些人可能会完全支持我的建议。然而,使用其他模块的通用函数或值的非常简单的方法是将其直接注入到通用工作区中。示例:
conftest.py:

import sys

def my_function():
   return 'my_function() called'

sys.modules['pytest'].common_funct = my_function
Run Code Online (Sandbox Code Playgroud)

测试我.py

import pytest

def test_A():
   print(pytest.common_funct())
Run Code Online (Sandbox Code Playgroud)

  • 这种方法允许您在测试函数之外使用辅助函数。例如,在定义 pytest.parametrization 参数时 (3认同)

小智 6

对于 pytest 7(至少),完成此操作的最简单方法是将以下配置添加到 pytest.ini 或 pyproject.toml 中。这会将tests目录添加到您的目录中sys.path并使相应的模块可用:

pythonpath = ["tests"]
Run Code Online (Sandbox Code Playgroud)

也许将测试助手与测试分开会更好,但这是一个风格问题。

例如,使用单独的目录,并使用它,pyproject.toml它看起来像这样:

[tool.pytest.ini_options]
pythonpath = ["test_helpers"]
testpaths = ["tests"]
addopts = [
    "--import-mode=importlib",
]
Run Code Online (Sandbox Code Playgroud)
src/
    ...
tests/
    ...
test_helpers/
    ...
Run Code Online (Sandbox Code Playgroud)

这配置了importlib 此处推荐的导入模式,其设计正是为了防止修改 sys.path,但是,如果您小心命名并且需要提取常见的测试助手,这可能是一个可行的选择。