返回: Pytest权威教程

插件编写

很容易为你自己的项目实现[本地conftest插件或可以在许多项目中使用的可[安装的插件,包括第三方项目。如果你只想使用但不能编写插件,请参阅[安装和使用插件。

插件包含一个或多个钩子(hooks)方法函数。[编写钩子(hooks)方法解释了如何自己编写钩子(hooks)方法函数的基础知识和细节。pytest通过调用以下插件的[指定挂钩来实现配置,收集,运行和报告的所有方面:

  • 内置插件:从pytest的内部_pytest目录加载。
  • 外部插件
  • conftest.py plugins:在测试目录中自动发现的模块

原则上,每个钩子(hooks)方法调用都是一个1:NPython函数调用,其中N是给定规范的已注册实现函数的数量。所有规范和实现都遵循pytest_前缀命名约定,使其易于区分和查找。

Pytest启动时的插件发现顺序

pytest通过以下方式在工具启动时加载插件模块:

  • 通过加载所有内置插件

  • 通过加载通过[setuptools入口点注册的所有插件。

  • 通过预扫描选项的命令行并在实际命令行解析之前加载指定的插件。-pname

  • 通过conftest.py命令行调用推断加载所有文件:

    • 如果未指定测试路径,则使用当前dir作为测试路径

    • 如果存在,则加载conftest.pytest*/conftest.py相对于第一个测试路径的目录部分。

      请注意,pytestconftest.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.pyPYTHONPATH或者也可能有其他文件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"],
)

如果以这种方式安装包,pytestmyproject.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-插件编写的更多相关文章

  1. Pytest权威教程04-断言的编写和报告

    目录 断言的编写和报告 使用assert语句进行断言 异常断言 警示断言 使用上下文对比 自定义断言对比信息 高级断言内省 返回: Pytest权威教程 断言的编写和报告 使用assert语句进行断言 ...

  2. Pytest权威教程(官方教程翻译)

    Pytest权威教程01-安装及入门 Pytest权威教程02-Pytest 使用及调用方法 Pytest权威教程03-原有TestSuite的执行方法 Pytest权威教程04-断言的编写和报告 P ...

  3. Pytest权威教程17-安装和使用插件

    目录 安装和使用插件 在测试模块或conftest文件中要求/加载插件 找出哪些插件是可用的 按名称取消/取消注册插件 返回: Pytest权威教程 安装和使用插件 本节讨论如何安装和使用第三方插件. ...

  4. Pytest权威教程19-编写钩子(Hooks)方法函数

    目录 编写钩子(Hooks)函数 钩子函数验证和执行 firstresult: 遇到第一个有效(非None)结果返回 hookwrapper:在其他钩子函数周围执行 钩子(Hooks)函数排序/调用示 ...

  5. Pytest权威教程21-API参考-04-钩子(Hooks)

    目录 钩子(Hooks) 引导时的Hook方法 初始化时的Hook方法 测试运行时的Hook方法 收集用例时的Hook方法 生成测试结果时的Hook方法 调试/交互Hook方法 返回: Pytest权 ...

  6. Pytest权威教程21-API参考-03-夹具(Fixtures)

    目录 夹具(Fixtures) @ pytest.fixture config.cache的 capsys capsysbinary capfd capfdbinary doctest_namespa ...

  7. Pytest权威教程01-安装及入门

    目录 安装及入门 安装 Pytest 创建你的第一个测试用例 执行多条测试用例 断言抛出了指定异常 使用类组织多条测试用例 函数测试中请求使用独立的临时目录 进一步阅读 返回: Pytest权威教程 ...

  8. Pytest权威教程05-Pytest fixtures:清晰 模块化 易扩展

    目录 Pytest fixtures:清晰 模块化 易扩展 Fixtures作为函数参数使用 Fixtures: 依赖注入的主要例子 conftest.py: 共享fixture函数 共享测试数据 生 ...

  9. Pytest权威教程13-Fixture方法及测试用例的参数化

    目录 Fixture方法及测试用例的参数化 @pytest.mark.parametrize:参数化测试函数 基本的pytest_generate_tests例子 更多示例 返回: Pytest权威教 ...

随机推荐

  1. 转 Html转pdf的工具——wkhtmltopdf

    下载地址:http://wkhtmltopdf.org/downloads.html 安装好以后需要在系统环境变量变量名为”Path”的后添加:;D:\wkhtmltopdf\bin 也就是你安装的目 ...

  2. 谷歌浏览器解决ajax跨域问题

    在用mui和H5+做混合开发,会利用HBuildx去真机调试,可真机调试总有问题所在,懂得人自然懂,而我们直接打开页面显示的只有一个静态的页面,是获取不到数据的在这里我想说的不是代码中利用jsonp, ...

  3. jQuery常用知识点大总结

    目录 jQuery jQuery介绍 jQuery的优势 jQuery的引入方式有两种: jQuery对象和dom对象 jQuery选择器 基本选择器(同css) 基本筛选器(选择之后进行过滤): 属 ...

  4. XML 约束

    XML约束 一.约束 约束:规定 xml 文档的书写规则 要求: 1.能够在 xml 中引入约束文档 2.能够简单的读懂约束文档 分类: 1.DTD:一种简单的约束技术(后缀.dtd) 2.Schem ...

  5. ECharts 散点图+百度地图(案例转载)

    转载来源:https://efe.baidu.com/blog/echarts-map-tutorial-2/ ECharts 实现地图散点图(下)  小红  2016-06-13  ECharts, ...

  6. mysql基础指令知识

    桌面指令(cmd)进入mysql客户端 第一步:安装mysql,配置环境变量 第二步:手动开启服务 第三步:输入如下指令: mysql [-h localhost -P 3306] -u  用户名 - ...

  7. Android笔记(六十四) android中的动画——补间动画(tweened animation)

    补间动画就是只需要定义动画开始和结束的位置,动画中间的变化由系统去补齐. 补间动画由一下四种方式: 1.AplhaAnimation——透明度动画效果 2.ScaleAnimation ——缩放动画效 ...

  8. (多核DSP快速入门)SYS/BIOS入门

    (多核DSP快速入门)SYS/BIOS入门   原创文章 转载请注册来源http://blog.csdn.net/tostq 系列教程目录:http://blog.csdn.net/tostq/art ...

  9. 2013.4.23 - KDD第五天

    今天晚上郭宇航师兄从外面回来问我那天找他什么事,然后我们就开始讨论KDD的第一个题目,其实第一个题目跟郭师兄的课题不太相关,本来想问他关于语义消 岐的那道题(第二道),不过第二题的内容我给忘了,然后我 ...

  10. python链式对比

    参考 https://www.cnblogs.com/shanghongyun/p/10519579.html 为什么Python中“2==2>1”结果为True在Python中,你可能会发现这 ...