良好的集成实践

用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 对战 python -m pytest 有关呼叫之间差异的详细信息 pytestpython -m pytest .

请注意,如果您正在使用 prepend import mode (这是默认值):您的测试文件必须具有 独特名称 ,因为 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ș .

注解

新的 --import-mode=importlib (见 导入模式 )不存在上述缺点,因为 sys.pathsys.modules 在导入测试模块时不会更改,因此强烈建议遇到此问题的用户尝试它并报告新选项是否对他们有效。

这个 src 但是,仍然强烈建议使用目录布局。

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

如果测试和应用程序模块之间有直接关系,并希望将它们与应用程序一起分发,则将测试目录内联到应用程序包中是很有用的:

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及更高版本。但是,从“内联测试”中,需要使用绝对导入来获取应用程序代码。

注解

prependappend 导入模式,如果pytest发现 "a/b/test_module.py" 测试文件递归到文件系统时,它确定导入名称,如下所示:

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

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

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

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

--import-mode=importlib 事情不那么复杂,因为pytest不需要改变 sys.pathsys.modules ,让事情不再那么令人惊讶。

托克斯

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