Pytest官方教程-23-优质集成实践

目录:

  1. 安装及入门
  2. 使用和调用方法
  3. 原有TestSuite使用方法
  4. 断言的编写和报告
  5. Pytest fixtures:清晰 模块化 易扩展
  6. 使用Marks标记测试用例
  7. Monkeypatching/对模块和环境进行Mock
  8. 使用tmp目录和文件
  9. 捕获stdout及stderr输出
  10. 捕获警告信息
  11. 模块及测试文件中集成doctest测试
  12. skip及xfail: 处理不能成功的测试用例
  13. Fixture方法及测试用例的参数化
  14. 缓存: 使用跨执行状态
  15. unittest.TestCase支持
  16. 运行Nose用例
  17. 经典xUnit风格的setup/teardown
  18. 安装和使用插件
  19. 插件编写
  20. 编写钩子(hook)方法
  21. 运行日志
  22. API参考
    1. 方法(Functions)
    2. 标记(Marks)
    3. 钩子(Hooks)
    4. 装置(Fixtures)
    5. 对象(Objects)
    6. 特殊变量(Special Variables)
    7. 环境变量(Environment Variables)
    8. 配置选项(Configuration Options)
  23. 优质集成实践
  24. 片状测试
  25. Pytest导入机制及sys.path/PYTHONPATH
  26. 配置选项
  27. 示例及自定义技巧
  28. Bash自动补全设置

使用pip安装包

对于开发,我们建议你将venv用于虚拟环境(或者用于Python 2.7的virtualenv),并使用 pip来安装应用程序和任何依赖项,以及pytest包本身。这可确保你的代码和依赖项与系统Python安装隔离。

接下来,setup.py使用以下最低内容将文件放在包的根目录中:

from setuptools import setup, find_packages

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

PACKAGENAME包裹的名称在哪里。然后,你可以通过从同一目录运行,以“可编辑”模式安装程序包:

pip install -e .

它允许你更改源代码(测试和应用程序)并随意重新运行测试。这与运行类似,或者使用符号链接将你的包安装到开发代码中。python setup.py develop``conda develop

Python测试发现的约定

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

  • 如果未指定参数,则从testpaths (如果已配置)或当前目录开始收集。或者,命令行参数可以用于目录,文件名或节点ID的任意组合。
  • 递归到目录,除非它们匹配norecursedirs
  • 在这些目录,搜索test_*.py*_test.py文件,由他们进口的测试包名。
  • 从这些文件中收集测试项目:
    • 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。pytest``python -m pytest

请注意,使用此方案时,你的测试文件必须具有唯一的名称,因为pytest将它们作为顶级模块导入,因为 没有包来从中获取完整的包名称。换句话说,在上面的示例中的试验文件将被导入为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

这种布局可以防止许多常见的陷阱,并且有很多好处,这在IonelCristianMă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仍将根据文件的存在执行测试包名称发现__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

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

与setuptools集成

你可以使用pytest-runner插件将测试运行集成到基于setuptools的项目中。

将此添加到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无需事先安装,无论如何都需要调用test命令。你还可以使用其他参数传递给pytest,例如测试目录或其他选项--addopts

你还可以setup.cfg通过将文件放入以下[tool:pytest]部分来指定文件中的其他pytest-ini选项:

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

手动整合

如果由于某种原因你不想/不能使用pytest-runner,你可以编写自己的setuptools测试命令来调用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

你可能感兴趣的:(Pytest官方教程-23-优质集成实践)