Pytest权威教程18-插件编写
插件编写
很容易为你自己的项目实现[本地conftest插件或可以在许多项目中使用的可[安装的插件,包括第三方项目。如果你只想使用但不能编写插件,请参阅[安装和使用插件。
插件包含一个或多个钩子(hooks)方法函数。[编写钩子(hooks)方法解释了如何自己编写钩子(hooks)方法函数的基础知识和细节。pytest通过调用以下插件的[指定挂钩来实现配置,收集,运行和报告的所有方面:
- 内置插件:从pytest的内部_pytest目录加载。
- 外部插件
- conftest.py plugins:在测试目录中自动发现的模块
原则上,每个钩子(hooks)方法调用都是一个1:NPython函数调用,其中N是给定规范的已注册实现函数的数量。所有规范和实现都遵循pytest_前缀命名约定,使其易于区分和查找。
Pytest启动时的插件发现顺序
pytest通过以下方式在工具启动时加载插件模块:
- 通过加载所有内置插件 
- 通过加载通过[setuptools入口点注册的所有插件。 
- 通过预扫描选项的命令行并在实际命令行解析之前加载指定的插件。 - -pname
- 通过 - conftest.py命令行调用推断加载所有文件:- 如果未指定测试路径,则使用当前dir作为测试路径 
- 如果存在,则加载 - conftest.py并- test*/conftest.py相对于第一个测试路径的目录部分。- 请注意,pytest - conftest.py在工具启动时没有在更深的嵌套子目录中找到文件。将- conftest.py文件保存在顶级测试或项目根目录中通常是个好主意。
 
- 通过递归加载文件中 - pytest_plugins变量指定的所有插件- conftest.py
conftest.py:本地每目录插件
本地conftest.py插件包含特定于目录的钩子(hooks)方法实现。Hook Session和测试运行活动将调用conftest.py靠近文件系统根目录的文件中定义的所有挂钩。实现pytest_runtest_setup钩子(hooks)方法的示例,以便在a子目录中调用而不是为其他目录调用:
a/conftest.py:
    def pytest_runtest_setup(item):
        # called for running each test in 'a' directory
        print("setting up",item)
a/test_sub.py:
    def test_sub():
        pass
test_flat.py:
    def test_flat():
        pass
以下是运行它的方法:
pytest test_flat.py --capture=no  # will not show "setting up"
pytest a/test_sub.py --capture=no  # will show "setting up"
注意
如果你的conftest.py文件不在python包目录中(即包含一个__init__.py),那么“import conftest”可能不明确,因为conftest.py你PYTHONPATH或者也可能有其他文件sys.path。因此,项目要么放在conftest.py包范围内,要么永远不从conftest.py文件中导入任何内容,这是一种很好的做法。
另请参见: pytest import mechanisms和sys.path / PYTHONPATH。
编写自己的插件
如果你想编写插件,可以从中复制许多现实示例如:
- 自定义集合示例插件: 在Yaml文件中指定测试的基本示例
- 内置插件,提供pytest自己的函数
- 许多外部插件提供额外的函数
所有这些插件都实现了[钩子(hooks)方法以扩展和添加函数。
注意
请务必查看优秀[的cookiecutter-pytest-plugin。
该模板提供了一个很好的起点,包括一个工作插件,使用tox运行的测试,一个全面的README文件以及一个预先配置的入口点。
另外考虑[将你的插件贡献给pytest-dev一旦它拥有一些非自己的快乐用户。
使你的插件可以被他人安装
如果你想让你的插件在外部可用,你可以为你的发行版定义一个所谓的入口点,以便pytest找到你的插件模块。入口点是[setuptools。pytest查找pytest11入口点以发现其插件,因此你可以通过在setuptools-invocation中定义插件来使插件可用:
# sample ./setup.py file
from setuptools import setup
setup(
    name="myproject",
    packages=["myproject"],
    # the following makes a plugin available to pytest
    entry_points={"pytest11": ["name_of_plugin = myproject.pluginmodule"]},
    # custom PyPI classifier for pytest plugins
    classifiers=["Framework :: Pytest"],
)
如果以这种方式安装包,pytest将myproject.pluginmodule作为可以定义[挂钩的插件加载。
注意
确保包含在[PyPI分类器
断言重写
其中一个主要特性pytest是使用普通的断言语句以及断言失败时表达式的详细内省。这是由“断言重写”提供的,它在编译为字节码之前修改了解析的AST。这是通过一个完成的PEP 302导入挂钩,在pytest启动时及早安装,并在导入模块时执行此重写。但是,由于我们不想测试不同的字节码,因此你将在生产中运行此挂钩仅重写测试模块本身以及作为插件一部分的任何模块。任何其他导入的模块都不会被重写,并且会发生正常的断言行为。
如果你在其他模块中有断言助手,你需要启用断言重写,你需要pytest在导入之前明确要求重写这个模块。
注册一个或多个要在导入时重写的模块名称。
此函数将确保此模块或程序包内的所有模块将重写其assert语句。因此,你应确保在实际导入模块之前调用此方法,如果你是使用包的插件,则通常在__init__.py中调用。
抛出: TypeError- 如果给定的模块名称不是字符串。
当你编写使用包创建的pytest插件时,这一点尤为重要。导入挂钩仅将入口点conftest.py中列出的文件和任何模块pytest11视为插件。作为示例,请考虑以下包:
pytest_foo/__init__.py
pytest_foo/plugin.py
pytest_foo/helper.py
使用以下典型setup.py提取物:
setup(...,entry_points={"pytest11": ["foo = pytest_foo.plugin"]},...)
在这种情况下,只会pytest_foo/plugin.py被重写。如果辅助模块还包含需要重写的断言语句,则需要在导入之前将其标记为这样。通过将其标记为在__init__.py模块内部进行重写,这是最简单的,当导入包中的模块时,将始终首先导入该模块。这种方式plugin.py仍然可以helper.py正常导入。然后,内容pytest_foo/__init__.py将需要如下所示:
import pytest
pytest.register_assert_rewrite("pytest_foo.helper")
在测试模块或conftest文件中要求/加载插件
你可以在测试模块或这样的conftest.py文件中要求插件:
pytest_plugins = ["name1","name2"]
加载测试模块或conftest插件时,也会加载指定的插件。任何模块都可以作为插件祝福,包括内部应用程序模块:
pytest_plugins = "myapp.testsupport.myplugin"
pytest_plugins变量是递归处理的,所以请注意,在上面的示例中,如果myapp.testsupport.myplugin也声明pytest_plugins,变量的内容也将作为插件加载,依此类推。
注意
pytest_plugins不建议使用非根conftest.py文件中使用变量的插件。
这很重要,因为conftest.py文件实现了每个目录的钩子(hooks)方法实现,但是一旦导入了插件,它就会影响整个目录树。为了避免混淆,不推荐pytest_plugins在任何conftest.py不在测试根目录中的文件中进行定义,并将发出警告。
这种机制使得在应用程序甚至外部应用程序中共享Fixture方法变得容易,而无需使用setuptools入口点技术创建外部插件。
导入的插件pytest_plugins也会自动标记为断言重写(请参阅参考资料pytest.register_assert_rewrite()在导入模块之前自行调用,也可以安排代码以延迟导入,直到注册插件为止。
按名称访问另一个插件
如果一个插件想要与另一个插件的代码协作,它可以通过插件管理器获得一个引用,如下所示:
plugin = config.pluginmanager.get_plugin("name_of_plugin")
如果要查看现有插件的名称,请使用该--trace-config选项。
注册为通用标记
测试插件
pytest附带一个名为的插件pytester,可帮助你为插件代码编写测试。默认情况下,该插件处于禁用状态,因此你必须先启用它,然后才能使用它。
你可以通过conftest.py将以下行添加到测试目录中的文件来执行此操作:
# content of conftest.py
pytest_plugins = ["pytester"]
或者,你可以使用命令行选项调用pytest。-ppytester
这将允许你使用testdirfixture来测试你的插件代码。
让我们用一个例子演示你可以用插件做什么。想象一下,我们开发了一个插件,它提供了一个hello产生函数的fixture,我们可以用一个可选参数调用这个函数。如果我们不提供值或者我们提供字符串值,它将返回字符串值。HelloWorld!``Hello{value}!
# -*- coding: utf-8 -*-
import pytest
def pytest_addoption(parser):
    group = parser.getgroup("helloworld")
    group.addoption(
        "--name",
        action="store",
        dest="name",
        default="World",
        help='Default "name" for hello().',
    )
@pytest.fixture
def hello(request):
    name = request.config.getoption("name")
    def _hello(name=None):
        if not name:
            name = request.config.getoption("name")
        return "Hello {name}!".format(name=name)
    return _hello
现在,testdirfixture提供了一个方便的API来创建临时conftest.py文件和测试文件。它还允许我们运行测试并返回一个结果对象,通过它我们可以断言测试的结果。
def test_hello(testdir):
    """Make sure that our plugin works."""
    # create a temporary conftest.py file
    testdir.makeconftest(
        """
 import pytest
 @pytest.fixture(params=[
 "Brianna",
 "Andreas",
 "Floris",
 ])
 def name(request):
 return request.param
 """
    )
    # create a temporary pytest test file
    testdir.makepyfile(
        """
 def test_hello_default(hello):
 assert hello() == "Hello World!"
 def test_hello_name(hello,name):
 assert hello(name) == "Hello {0}!".format(name)
 """
    )
    # run all tests with pytest
    result = testdir.runpytest()
    # check that all 4 tests passed
    result.assert_outcomes(passed=4)
另外,可以在运行pytest之前复制示例文件夹的示例
# content of pytest.ini
[pytest]
pytester_example_dir = .
# content of test_example.py
def test_plugin(testdir):
    testdir.copy_example("test_example.py")
    testdir.runpytest("-k","test_example")
def test_example():
    pass
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y,pytest-4.x.y,py-1.x.y,pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR,inifile: pytest.ini
collected 2 items
test_example.py ..                                                  [100%]
============================= warnings summary =============================
test_example.py::test_plugin
  $REGENDOC_TMPDIR/test_example.py:4: PytestExperimentalApiWarning: testdir.copy_example is an experimental api that may change over time
    testdir.copy_example("test_example.py")
-- Docs: https://docs.pytest.org/en/latest/warnings.html
=================== 2 passed,1 warnings in 0.12 seconds ===================
有关runpytest()返回的结果对象及其提供的方法的更多信息,请查看RunResult文档。
Pytest权威教程18-插件编写的更多相关文章
- Pytest权威教程04-断言的编写和报告
		目录 断言的编写和报告 使用assert语句进行断言 异常断言 警示断言 使用上下文对比 自定义断言对比信息 高级断言内省 返回: Pytest权威教程 断言的编写和报告 使用assert语句进行断言 ... 
- Pytest权威教程(官方教程翻译)
		Pytest权威教程01-安装及入门 Pytest权威教程02-Pytest 使用及调用方法 Pytest权威教程03-原有TestSuite的执行方法 Pytest权威教程04-断言的编写和报告 P ... 
- Pytest权威教程17-安装和使用插件
		目录 安装和使用插件 在测试模块或conftest文件中要求/加载插件 找出哪些插件是可用的 按名称取消/取消注册插件 返回: Pytest权威教程 安装和使用插件 本节讨论如何安装和使用第三方插件. ... 
- Pytest权威教程19-编写钩子(Hooks)方法函数
		目录 编写钩子(Hooks)函数 钩子函数验证和执行 firstresult: 遇到第一个有效(非None)结果返回 hookwrapper:在其他钩子函数周围执行 钩子(Hooks)函数排序/调用示 ... 
- Pytest权威教程21-API参考-04-钩子(Hooks)
		目录 钩子(Hooks) 引导时的Hook方法 初始化时的Hook方法 测试运行时的Hook方法 收集用例时的Hook方法 生成测试结果时的Hook方法 调试/交互Hook方法 返回: Pytest权 ... 
- Pytest权威教程21-API参考-03-夹具(Fixtures)
		目录 夹具(Fixtures) @ pytest.fixture config.cache的 capsys capsysbinary capfd capfdbinary doctest_namespa ... 
- Pytest权威教程01-安装及入门
		目录 安装及入门 安装 Pytest 创建你的第一个测试用例 执行多条测试用例 断言抛出了指定异常 使用类组织多条测试用例 函数测试中请求使用独立的临时目录 进一步阅读 返回: Pytest权威教程 ... 
- Pytest权威教程05-Pytest fixtures:清晰 模块化 易扩展
		目录 Pytest fixtures:清晰 模块化 易扩展 Fixtures作为函数参数使用 Fixtures: 依赖注入的主要例子 conftest.py: 共享fixture函数 共享测试数据 生 ... 
- Pytest权威教程13-Fixture方法及测试用例的参数化
		目录 Fixture方法及测试用例的参数化 @pytest.mark.parametrize:参数化测试函数 基本的pytest_generate_tests例子 更多示例 返回: Pytest权威教 ... 
随机推荐
- Spring中ApplicationContextAware的作用
			ApplicationContextAware 通过它Spring容器会自动把上下文环境对象调用ApplicationContextAware接口中的setApplicationContext方法. ... 
- 主机与虚拟机ping不通问题
			在win10 上利用vm安装linux虚拟机,物理机与虚拟机之间不能ping通,除了检查物理机与虚拟机防火墙状态外,还要注意物理机设置的ip与虚拟机设置的ip,要在同一个ip段 
- 【洛谷 P2408】 不同子串个数(后缀自动机)
			题目链接 裸体就是身体. 建出\(SAM\),\(DAG\)上跑\(DP\),\(f[u]=1+\sum_{(u,v)\in DAG}f[v]\) 答案为\(f[1]-1\)(因为根节点没有字符) # ... 
- flex 布局学习
			flex 布局学习 寻根溯源话布局 一切都始于这样一个问题:怎样通过 CSS 简单而优雅的实现水平.垂直同时居中.记得刚开始学习 CSS 的时候,看到 float 属性不由得感觉眼前一亮,顺理成章的联 ... 
- SAP NOTE 1999997 - FAQ: SAP HANA Memory
			Symptom You have questions related to the SAP HANA memory. You experience a high memory utilization ... 
- sql 树形递归查询
			sql 树形递归查询: with ProductClass(ClassId,ClassName) as ( union all select c.ClassId,c.ClassName from Cl ... 
- orangepi自启动打开一个终端并且运行脚本
			第一步: 在 /home/pi/.config 下创建一个文件夹,名称为 autostart,如果已经存在则不需要创建 第二步: 在autostart文件夹中创建桌面启动项文件,(文件名以 .desk ... 
- ClickHouse学习笔记
			1. 概述 ClickHouse是一个用于联机分析(OLAP:Online Analytical Processing)的列式数据库管理系统(DBMS:Database Management Syst ... 
- uc/xi
			一个较为通用的定义为:嵌入式系统是对对象进行自动控制而使其具有智能化并可嵌入对象体系统中的专用计算机系统. 实时性:目前,嵌入式系统广泛应用于生产过程控制.数据采集.传输通信等场合,这些应用的共同特点 ... 
- Git-fatal:remote error:You can't push to git://github.com/username/*.git use https:
			注意不是git://github.com/cs942651107/TestCode.git 一个:一个@协议不一样,:的不能push 关联远程库git remote add origin git ... 
