安装和使用插件
本节讨论如何安装和使用第三方插件。有关编写自己的插件的信息,请参阅编写插件。
安装第三方插件可以通过以下方式轻松完成pip:
Copypip install pytest-NAME
pip uninstall pytest-NAME
如果安装了插件,则pytest自动查找并集成它,无需激活它。
这是一些常用插件列表:
pytest-django:为django应用程序编写测试。
pytest-twisted:为twisted应用程序编写测试,启动反应堆并处理测试函数的延迟。
pytest-cov:覆盖率报告,与分布式测试兼容
pytest-xdist:将测试分发到CPU和远程主机,以盒装模式运行,允许分段故障,在looponfailing模式下运行,自动重新运行文件更改的失败测试。
pytest-instafail:在测试运行期间报告失败。
pytest-bdd使用行为驱动的测试编写测试。
pytest-timeout:根据函数标记或全局定义进行超时测试。
pytest-pep8:--pep8启用PEP8合规性检查的选项。
pytest-flakes:用pyflakes检查源代码。
oejskit:在实时浏览器中运行javascript unittests的插件。
要查看具有针对不同pytest和Python版本的最新测试状态的所有插件的完整列表,请访问plugincompat。
你还可以通过pytest-pypi.org搜索发现更多插件。
在测试模块或conftest文件中要求/加载插件
你可以在测试模块或conftest文件中要求插件,如下所示:
Copypytest_plugins = ("myapp.testsupport.myplugin",)
加载测试模块或conftest插件时,也会加载指定的插件。
注意: pytest_plugins不建议使用非根conftest.py文件中使用变量的插件。请参阅“编写插件”部分中的完整说明。
注意: 该名称pytest_plugins是保留的,不应用作自定义插件模块的名称。
找出哪些插件是可用的
如果要查找环境中哪些插件处于可用状态,可以键入:
Copypytest --trace-config
并将获得一个扩展的测试标题,显示激活的插件及其名称。它还会在加载时打印本地插件aka[conftest.py文件。
按名称取消/取消注册插件
你可以阻止插件加载或取消注册:
Copypytest -p no:NAME
这意味着任何后续尝试激活/加载命名插件都不起作用。
如果要无条件禁用项目插件,可以将此选项添加到pytest.ini文件中:
Copy[pytest]addopts = -p no:NAME
或者,仅在某些环境中禁用它(例如在CI服务器中),可以将PYTEST_ADDOPTS环境变量设置为。-p no:name
请参阅查找有关如何获取插件名称的活动插件。
插件编写
很容易为你自己的项目实现[本地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相对于第一个测试路径的目录部分。
请注意,pytestconftest.py在工具启动时没有在更深的嵌套子目录中找到文件。将conftest.py文件保存在顶级测试或项目根目录中通常是个好主意。
通过递归加载文件中pytest_plugins变量指定的所有插件conftest.py
conftest.py:本地每目录插件
本地conftest.py插件包含特定于目录的钩子(hooks)方法实现。Hook Session和测试运行活动将调用conftest.py靠近文件系统根目录的文件中定义的所有挂钩。实现pytest_runtest_setup钩子(hooks)方法的示例,以便在a子目录中调用而不是为其他目录调用:
Copya/conftest.py:
defpytest_runtest_setup(item):
# called for running each test in 'a' directoryprint("setting up",item)
a/test_sub.py:
deftest_sub():
pass
test_flat.py:
deftest_flat():
pass
以下是运行它的方法:
Copypytest 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中定义插件来使插件可用:
Copy# sample ./setup.py filefrom 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视为插件。作为示例,请考虑以下包:
Copypytest_foo/__init__.py
pytest_foo/plugin.py
pytest_foo/helper.py
使用以下典型setup.py提取物:
Copysetup(...,entry_points={"pytest11": ["foo = pytest_foo.plugin"]},...)
在这种情况下,只会pytest_foo/plugin.py被重写。如果辅助模块还包含需要重写的断言语句,则需要在导入之前将其标记为这样。通过将其标记为在__init__.py模块内部进行重写,这是最简单的,当导入包中的模块时,将始终首先导入该模块。这种方式plugin.py仍然可以helper.py正常导入。然后,内容pytest_foo/__init__.py将需要如下所示:
Copyimport pytest
pytest.register_assert_rewrite("pytest_foo.helper")
在测试模块或conftest文件中要求/加载插件
你可以在测试模块或这样的conftest.py文件中要求插件:
Copypytest_plugins = ["name1","name2"]
加载测试模块或conftest插件时,也会加载指定的插件。任何模块都可以作为插件祝福,包括内部应用程序模块:
Copypytest_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()在导入模块之前自行调用,也可以安排代码以延迟导入,直到注册插件为止。
按名称访问另一个插件
如果一个插件想要与另一个插件的代码协作,它可以通过插件管理器获得一个引用,如下所示:
Copyplugin = config.pluginmanager.get_plugin("name_of_plugin")
如果要查看现有插件的名称,请使用该--trace-config选项。
注册为通用标记
测试插件
pytest附带一个名为的插件pytester,可帮助你为插件代码编写测试。默认情况下,该插件处于禁用状态,因此你必须先启用它,然后才能使用它。
你可以通过conftest.py将以下行添加到测试目录中的文件来执行此操作:
Copy# content of conftest.py
pytest_plugins = ["pytester"]
或者,你可以使用命令行选项调用pytest。-ppytester
这将允许你使用testdirfixture来测试你的插件代码。
让我们用一个例子演示你可以用插件做什么。想象一下,我们开发了一个插件,它提供了一个hello产生函数的fixture,我们可以用一个可选参数调用这个函数。如果我们不提供值或者我们提供字符串值,它将返回字符串值。HelloWorld!``Hello{value}!
Copy# -*- coding: utf-8 -*-import pytest
defpytest_addoption(parser):
group = parser.getgroup("helloworld")
group.addoption(
"--name",
action="store",
dest="name",
default="World",
help='Default "name" for hello().',
)
@pytest.fixturedefhello(request):
name = request.config.getoption("name")
def_hello(name=None):
ifnot name:
name = request.config.getoption("name")
return"Hello {name}!".format(name=name)
return _hello
现在,testdirfixture提供了一个方便的API来创建临时conftest.py文件和测试文件。它还允许我们运行测试并返回一个结果对象,通过它我们可以断言测试的结果。
Copydeftest_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之前复制示例文件夹的示例
Copy# content of pytest.ini[pytest]pytester_example_dir = .
Copy# content of test_example.pydeftest_plugin(testdir):
testdir.copy_example("test_example.py")
testdir.runpytest("-k","test_example")
deftest_example():
pass
Copy$ 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文档。
实战案例
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
![](https://img-blog.csdnimg.cn/img_convert/ca75842cf4804604b64376a5d728b248.jpeg)
![](https://img-blog.csdnimg.cn/img_convert/a47adcc706a34354868d5b668ceb1baa.jpeg)
如果对你有帮助的话,点个赞收个藏,给作者一个鼓励。也方便你下次能够快速查找。
如有不懂还要咨询下方小卡片,博主也希望和志同道合的测试人员一起学习进步
在适当的年龄,选择适当的岗位,尽量去发挥好自己的优势。
我的自动化测试开发之路,一路走来都离不每个阶段的计划,因为自己喜欢规划和总结,
测试开发视频教程、学习笔记领取传送门!!!
![](https://img-blog.csdnimg.cn/img_convert/e551a30416d62c93e61b42aff6da092d.gif)