良好的集成实践

用PIP安装包

对于开发,我们建议您使用 venv 对于虚拟环境和 pip 用于安装应用程序和任何依赖项,以及 pytest 包装本身。这样可以确保代码和依赖项与系统python安装分离。

其次,放置一个 setup.py 文件位于包根目录中,最小内容如下:

from setuptools import setup, find_packages

setup(name="PACKAGENAME", packages=find_packages())

PACKAGENAME

pip install -e .

python setup.py developconda develop 它使用开发代码的符号链接来安装包。

Python测试发现的约定

pytest 实现以下标准测试发现:

  • 如果未指定参数,则集合从开始 testpaths (如果配置)或当前目录。或者,命令行参数可以用于目录、文件名或节点ID的任意组合。

  • 递归到目录中,除非它们匹配 norecursedirs .

  • 在这些目录中,搜索 test_*.py*_test.py 文件,由其导入 test package name .

  • 从这些文件中收集测试项:

    • test 在类之外加前缀的测试函数或方法

    • test 内置前缀测试函数或方法 Test 带前缀的测试类(不带 __init__ 方法)

有关如何自定义测试发现的示例 更改标准(python)测试发现 .

在python模块中, pytest 同时发现使用标准的测试 unittest.TestCase 子类化技术。

选择测试布局/导入规则

pytest 支持两种常见的测试布局:

应用程序代码之外的测试

如果您有许多功能测试或者出于其他原因希望将测试与实际应用程序代码分开(通常是个好主意),将测试放入实际应用程序代码之外的额外目录中可能会很有用:

setup.py
mypkg/
    __init__.py
    app.py
    view.py
tests/
    test_app.py
    test_view.py
    ...

这有以下好处:

  • 您的测试可以在执行后针对已安装的版本运行 pip install . .

  • 您的测试可以在执行后使用可编辑的安装在本地副本上运行 pip install --editable . .

  • 如果你没有 setup.py 文件和依赖于这样一个事实:默认情况下,python将当前目录放入 sys.path 要导入包,可以执行 python -m pytest 直接对本地副本执行测试,而不使用 pip .

注解

Pytest导入机制和 sys.path/PYTHONPATH 有关调用之间差异的详细信息 pytestpython -m pytest .

请注意,使用此方案,测试文件必须 独特名称 ,因为 pytest 将它们导入为 top-level 模块,因为没有要从中派生完整包名称的包。换句话说,上面示例中的测试文件将导入为 test_apptest_view 通过添加顶级模块 tests/sys.path .

如果需要具有相同名称的测试模块,可以添加 __init__.py 文件到您 tests 文件夹和子文件夹,将其更改为包:

setup.py
mypkg/
    ...
tests/
    __init__.py
    foo/
        __init__.py
        test_view.py
    bar/
        __init__.py
        test_view.py

现在,pytest将模块加载为 tests.foo.test_viewtests.bar.test_view ,允许使用相同名称的模块。但现在这引入了一个微妙的问题:为了从 tests 目录,pytest将存储库的根目录 sys.path 这增加了现在 mypkg 也很重要。如果您使用类似 tox 在虚拟环境中测试包,因为您要测试 安装 包的版本,而不是存储库中的本地代码。

在这种情况下,它是 强烈地 建议使用 src 应用程序根包位于根目录的子目录中的布局::

setup.py
src/
    mypkg/
        __init__.py
        app.py
        view.py
tests/
    __init__.py
    foo/
        __init__.py
        test_view.py
    bar/
        __init__.py
        test_view.py

这种布局可以防止许多常见的陷阱,并具有许多好处,这在这篇优秀的文章中可以更好地解释。 blog post by Ionel Cristian Mărieș .

测试作为应用程序代码的一部分

如果测试和应用程序模块之间存在直接关系,并且希望将它们与应用程序一起分发,则将测试目录嵌入到应用程序包中非常有用:

setup.py
mypkg/
    __init__.py
    app.py
    view.py
    test/
        __init__.py
        test_app.py
        test_view.py
        ...

在这个方案中,使用 --pyargs 选项:

pytest --pyargs mypkg

pytest 会发现哪里 mypkg 安装并从中收集测试。

请注意,此布局还与 src 上一节中提到的布局。

注解

可以为应用程序使用python3命名空间包(pep420),但pytest仍将执行 test package name 基于存在的 __init__.py 文件夹。如果您使用上述两种推荐的文件系统布局之一,但不使用 __init__.py 目录中的文件应该只适用于python3.3及更高版本。但是,从“内联测试”中,需要使用绝对导入来获取应用程序代码。

注解

如果 pytest 在递归到文件系统时查找“a/b/test_module.py”测试文件,它确定导入名称如下:

  • 决定 basedir :这是第一个不包含 __init__.py . 如果两者兼而有之 ab 包含一个 __init__.py 文件,然后是的父目录 a 将成为 basedir .

  • 执行 sys.path.insert(0, basedir) 使测试模块可以在完全限定的导入名称下导入。

  • import a.b.test_module 其中路径由转换路径分隔符确定 / 转换为“.”字符。这意味着您必须遵循将目录和文件名直接映射到导入名称的惯例。

这种有点进化的导入技术的原因是,在较大的项目中,多个测试模块可能互相导入,因此派生规范的导入名称有助于避免意外,例如测试模块被导入两次。

托克斯

一旦您完成了您的工作,并希望确保您的实际包通过了您可能希望查看的所有测试 tox 虚拟环境测试自动化工具及其 pytest support . tox帮助您使用预定义的依赖项设置virtualenv环境,然后使用选项执行预先配置的测试命令。它将针对已安装的包运行测试,而不是针对源代码签出运行测试,这有助于检测包故障。

与设置工具集成/ python setup.py test / pytest-runner

可以将测试运行集成到基于安装工具的项目中, pytest-runner 插件。

将此添加到 setup.py 文件:

from setuptools import setup

setup(
    # ...,
    setup_requires=["pytest-runner", ...],
    tests_require=["pytest", ...],
    # ...,
)

并在中创建别名 setup.cfg 文件:

[aliases]
test=pytest

如果现在键入:

python setup.py test

这将使用 pytest-runner . 因为这是 pytest 调用测试命令不需要预先安装。您还可以使用 --addopts .

您还可以在 setup.cfg 把它们放进 [tool:pytest] 章节:

[tool:pytest]
addopts = --verbose
python_files = testing/*/*.py

手动集成

如果出于某种原因您不想/不能使用 pytest-runner ,您可以编写自己的设置工具测试命令来调用pytest。

import sys

from setuptools.command.test import test as TestCommand


class PyTest(TestCommand):
    user_options = [("pytest-args=", "a", "Arguments to pass to pytest")]

    def initialize_options(self):
        TestCommand.initialize_options(self)
        self.pytest_args = ""

    def run_tests(self):
        import shlex

        # import here, cause outside the eggs aren't loaded
        import pytest

        errno = pytest.main(shlex.split(self.pytest_args))
        sys.exit(errno)


setup(
    # ...,
    tests_require=["pytest"],
    cmdclass={"pytest": PyTest},
)

现在,如果你运行:

python setup.py test

这将下载 pytest 如果需要,然后按预期运行测试。您可以使用 --pytest-args-a 命令行选项。例如::

python setup.py test -a "--durations=5"

相当于运行 pytest --durations=5 .