Installing and Using plugins¶
可以通过pip来安装和卸载插件:
pip install pytest-Name
pip uninstall pytest-Name
一些插件例子:
- pytest-django
- pytest-twisted
- pytest-cov
- pytest-xdist:将测试分散到多个CPU上执行
- pytest-instafail:即时报告错误信息
- pytest-bdd:支持behaviour-driven testing风格
- pytest-timeout:支持测试timeout
- pytest-pip8:添加–pep8选项
- pytest-flakes:使用pyflakes来检查源码
- oejskit:在现行浏览器中运行javascript unittest
可以在测试模块或者conftest.py中使用pytest_plugins来声明所需要的插件:
pytest_plugins = ("myapp.testsupport.myplugin",)
在根目录的conftest.py下使用pytest_plugins的支持以及被废弃了
打印导入的插件列表:pytest --trace-config
在命令行禁用一个插件:pytest -p no:NAME
。在环境变量PYTEST_ADDOPTS 指定-p no:NAME
可以达到相同效果。
在pytest.ini中禁用插件:
[pytest]
addopts = -p no:NAME
Writing plugins
一个插件包含一至多个钩子函数。pytest实现了配置、收集、运行和报告的方方面面,并提供很多精细的钩子,可供不同的插件来关联。pytest会查找以下插件:
- 自带插件,在pytest的_pytest目录
- 外部插件,通过setuptools进入点来判断
- conftest.py插件,在众多test目录中发掘
pytest启动时加载插件的顺序:
- 自带插件
- 外部插件
- 命令行-p name指定的插件
- 查找conftest.py并加载其中的pytest_plugins
- 如果没有在命令行指定运行目录,那么当前目录会被作为运行目录来查找conftest.py
- 如果指定了,那么会从指定目录的相对路径运行conftest.py以及test*/contest.py
conftest.py可以对在同一目录下的测试进行自定义。
不要import conftest。如果真的需要import,保证conftest.py在一个package内。
可以从既有的例子中找容易学的来开始写自己的插件:
- https://docs.pytest.org/en/latest/example/nonpython.html#yaml-plugin
- https://github.com/pytest-dev/cookiecutter-pytest-plugin
对于setuptools打包的插件,可以通过提供pytest11进入点来让pytest发现这个插件,一个例子:
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加载插件了。
pytest通过PEP 302提供的import钩子来改写AST中的assert语句。pytest只改写测试模块,或者插件中的模块。但是通过register_assert_rewrite可以主动要求pytest来改写其中的assert。
在conftest.py指定插件的例子:
pytest_plugins = ["name1", "name2"]
pytest_plugins = "myapp.testsupport.myplugin"
注意,这个过程可以是递归的,当myapp.testsupport.myplugin也定义了pytest_plugins,那么那些pytest_plugins也会被导入。
可以使用:plugin = config.pluginmanager.get_plugin("name_of_plugin")
在一个插件中访问其他插件。
可以在插件中注册markers:
def pytest_configure(config):
config.addinivalue_line("markers", "cool_marker: this one is for cool tests.")
config.addinivalue_line(
"markers", "mark_with(arg, arg2): this marker takes arguments."
)
pytester这个插件可以用来测试其他插件。在命令行使用-p pyteste
,或者在testing目录的conftest.py中添加:
# content of conftest.py
pytest_plugins = ["pytester"]
pytest会调用插件中的钩子函数,以 pytest_collection_modifyitems(session, config, items)为例:
def pytest_collection_modifyitems(config, items):
# called after collection is completed
# you can modify the ``items`` list
...
pytest会传config以及items参数,而不会传session参数。
pytest的钩子一般返回一个结果列表,有些钩子使用firstresult=True来告知pytest它只在第一轮运行中被调用,而不再重试中生效。
pytest还提供钩子封装,可以用来封装其他钩子,不过操作比较复杂,需要通过generator来操作:
@pytest.hookimpl(hookwrapper=True)
def pytest_pyfunc_call(pyfuncitem):
do_something_before_next_hook_executes()
outcome = yield
# outcome.excinfo may be None or a (cls, val, tb) tuple
res = outcome.get_result() # will raise if outcome was exception
post_process_result(res)
outcome.force_result(new_res) # to override the return value to the plugin system
一个钩子可能会关联不同的函数,可以通过选项来指定其执行顺序:
# Plugin 1
@pytest.hookimpl(tryfirst=True)
def pytest_collection_modifyitems(items):
# will execute as early as possible
...
# Plugin 2
@pytest.hookimpl(trylast=True)
def pytest_collection_modifyitems(items):
# will execute as late as possible
...
# Plugin 3
@pytest.hookimpl(hookwrapper=True)
def pytest_collection_modifyitems(items):
# will execute even before the tryfirst one above!
outcome = yield
# will execute after all non-hookwrappers executed
conftest.py中可以指定其他plugin实现的钩子:pytest_addhooks(pluginmanager: PytestPluginManager) → None
一个插件可以通过pytest_addoption暴露一个命令行选项,供另一个插件来提供值。
可以延后钩子的注册,以免钩子所属的插件未安装:
class DeferPlugin:
"""Simple plugin to defer pytest-xdist hook functions."""
def pytest_testnodedown(self, node, error):
"""standard xdist hook function.
"""
def pytest_configure(config):
if config.pluginmanager.hasplugin("xdist"):
config.pluginmanager.register(DeferPlugin())
其他
pytest的插件合集:http://plugincompat.herokuapp.com/,目前有976个
(本篇完)
2020-07-25更新
如果要运行async测试用例,则会出现以下提示:
You need to install a suitable plugin for your async framework, for example:
- pytest-asyncio
- pytest-trio
- pytest-tornasync
- pytest-twisted
warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid)))
解决办法之一是把pytest-asyncio装上
参考
(更新完)