博客
关于我
Pytest插件开发
阅读量:417 次
发布时间:2019-03-06

本文共 8464 字,大约阅读时间需要 28 分钟。

目录

Pytest测试框架功能非常多,它其实就是由一组插件组成的,具有大量的插件,可以通过插件来扩展、定制功能,能满足大部分的测试需求。本文介绍pytest插件的开发方法,帮助更好的理解pytest测试框架。

pytest插件介绍

pytest的三种插件

pytest插件通过hook函数来实现,pytest主要包括以下三种插件

  • 内置插件:pytest内部的_pytest目录中加载:\Lib\site-packages\_pytest\hookspec.py
  • 外部插件:pip install 插件,通过setuptools的Entry points机制来发现外部插件,可用插件列表:
  • 本地插件:conftest.py插件,pytest自动模块发现机制,在项目根目录下的conftest文件起到全局作用,在项目下的子目录中的conftest.py文件作用范围只能在该层级及以下目录生效。

他们的加载顺序为:

  1. 内置插件
  2. 外部插件
  3. 本地插件

pytest的hook函数

hook函数(钩子函数)是程序中预留的函数(相当于暴露了一个钩子),如果我们需要在程序某个步骤执行某个操作,我们就直接重写特定的钩子函数(挂载到钩子上),这样就实现了我们要增加的功能。没有挂载或者注册钩子时,它就是空的,也就是没有执行任何操作。

Pytest 的hook函数可查看\Lib\site-packages\_pytest\hookspec.py 文件, Pytest hook函数的执行顺序如下():

root└── pytest_cmdline_main    ├── pytest_plugin_registered    ├── pytest_configure    │   └── pytest_plugin_registered    ├── pytest_sessionstart    │   ├── pytest_plugin_registered    │   └── pytest_report_header    ├── pytest_collection    │   ├── pytest_collectstart    │   ├── pytest_make_collect_report    │   │   ├── pytest_collect_file    │   │   │   └── pytest_pycollect_makemodule    │   │   └── pytest_pycollect_makeitem    │   │       └── pytest_generate_tests    │   │           └── pytest_make_parametrize_id    │   ├── pytest_collectreport    │   ├── pytest_itemcollected    │   ├── pytest_collection_modifyitems    │   └── pytest_collection_finish    │       └── pytest_report_collectionfinish    ├── pytest_runtestloop    │   └── pytest_runtest_protocol    │       ├── pytest_runtest_logstart    │       ├── pytest_runtest_setup    │       │   └── pytest_fixture_setup    │       ├── pytest_runtest_makereport    │       ├── pytest_runtest_logreport    │       │   └── pytest_report_teststatus    │       ├── pytest_runtest_call    │       │   └── pytest_pyfunc_call    │       ├── pytest_runtest_teardown    │       │   └── pytest_fixture_post_finalizer    │       └── pytest_runtest_logfinish    ├── pytest_sessionfinish    │   └── pytest_terminal_summary    └── pytest_unconfigure

我们可以对上面的hook函数进行改写,实现某些功能。我在以前的pytest文章中()介绍了fixture 插件的用法,fixture实现的功能其实也对pytest的hook函数进行了改写,比如pytest_generate_tests,pytest_sessionstart等hook函数,大家如果感兴趣可以查看源码:\Lib\site-packages\_pytest\fixtures.py

pluggy插件系统

前面简要介绍了pytest插件,这些插件是怎么管理的呢?pytest的大量插件使用pluggy进行管理,pluggy是pytest使用的一个插件系统,用于pytest插件的管理和钩子调用。也就是说,pluggy使pytest具有了钩子功能,实现主机(主程序)与插件的连接。

pytest插件:中文编码

先写一个测试用例test_hook.py:

#!/usr/bin/python3#-*-coding:utf-8-*-import pytest@pytest.mark.parametrize("name",["张三","李四"])def test_name(name):    print(name)

执行:

$ pytest -vs test_hook.py::test_name

结果(只截取部分):

test_hook.py::test_name[\u5f20\u4e09] 张三PASSEDtest_hook.py::test_name[\u674e\u56db] 李四PASSED

我们发现用例名字编码格式为Unicode,无法显示中文。

怎么解决呢?我们可以对pytest hook函数pytest_collection_modifyitems()进行重写:

def pytest_collection_modifyitems(    session: "Session", config: "Config", items: List["Item"]) -> None:    """ called after collection has been performed, may filter or re-order    the items in-place.    :param _pytest.main.Session session: the pytest session object    :param _pytest.config.Config config: pytest config object    :param List[_pytest.nodes.Item] items: list of item objects    """

根据注释我们知道这个hook函数在用例收集完成后进行调用,可对用例进行过滤或者重新排序(修改用例执行顺序的pytest-ordering插件就修改了这个hook函数)

接下来开始修改这个hook函数,对用例名进行解码并反转用例顺序,在测试用例同级目录下新建conftest.py函数,修改代码如下:

#!/usr/bin/python3#-*-coding:utf-8-*-from typing import Listdef pytest_collection_modifyitems(session, config, items: List):    for item in items:        item.name = item.name.encode('utf-8').decode('unicode-escape')        item._nodeid = item.nodeid.encode('utf-8').decode('unicode-escape')    items.reverse()

items就是收集到的测试用例,可对它进行各种操作。

再次执行:

$ pytest -vs test_hook.py::test_name

结果(只截取部分):

test_hook.py::test_name[李四] 李四PASSEDtest_hook.py::test_name[张三] 张三PASSED

解码成功,并且顺序反转了。

添加命令行参数

通过改写hook函数pytest_addoption()可以实现添加自定义的命令行参数,几乎每个pytest 插件都会使用这个hook方法。下面在conftest.py中改写pytest_addoption()方法:

# 添加一个命令行参数def pytest_addoption(parser, pluginmanager):    mygroup = parser.getgroup("testgroup") #group将下面所有的 optiongroup都展示在这个group下。    mygroup.addoption("--env", #注册一个命令行选项        default='test',  # 参数的默认值        dest = 'env',  # 存储的变量        help = 'set your run env' #帮助提示参数的描述信息    )

然后我们在命令行中输入pytest -h,在打印的帮助信息中,我们可以看到添加的自定义参数:

testgroup:  --env=ENV             set your run env

接下来获取这个参数,在conftest.py中添加如下代码:

@pytest.fixture(scope='session')def cmdoption(request):    env = request.config.getoption("--env", default='test')    if env == "test":        print("test环境")        datapath = "data/test/data.yml"    if env == "develop":        print("开发环境")        datapath = "data/develop/data.yml"    with open(datapath) as f:        datas = yaml.safe_load(f)    return env,datas

测试数据:

编写测试用例:

def test_env(cmdoption):    env,datas = cmdoption    host = datas['env']['host']    port = datas['env']['port']    url = str(host) + ":" + str(port)    print(url)

执行测试用例:

$ pytest -vs test_hook.py::test_env

结果(只截取部分):

test_hook.py::test_env test环境http://192.168.11.1:4567PASSED

传递参数:

$ pytest -vs --env develop test_hook.py::test_env

结果(只截取部分):

test_hook.py::test_env 开发环境http://192.168.0.1:1234PASSED

传递成功

打包发布

你的Python插件开发完成后,可以对它进行打包发布,方便给别人使用,打包后也可以发布代码到到PyPI上,可参考Python打包文档: ,下面介绍Python打包过程。

1、创建包文件

pytest-encode/├── LICENSE├── README.md├── setup.py├── pytest_encode/│   └── __init__.py└──tests/    └── test_encode.py

setup.py是一个构建脚本:

import setuptoolssetuptools.setup(    name="pytest-encode", # Replace with your own username    version="0.0.1",    author="hiyongz",    author_email="zhiyo2016@163.com@example.com",    description="set your encoding",    long_description="show Chinese for your mark.parametrize().",    url="https://github.com/pypa/sampleproject",    project_urls={        "Bug Tracker": "https://github.com/pypa/sampleproject/issues",    },    classifiers=[        "Programming Language :: Python :: 3",        "Framework :: Pytest",        "Topic :: Software Develoment :: Testing",    ],    license='MIT License',    packages=['pytest_encode'],    keywords=[        "pytest",        "py.test",        "pytest_encode",    ],    install_requires=[        'pytest'    ],    python_requires=">=3.6",    # 入口模块或者入口函数    entry_points={        'pytest11':[            'pytest-encode = pytest_encode'        ]    },           zip_safe=False,)
  1. 其中依赖包可以通过如下命令生成:
$ pip freeze >requirements.txt
  1. entry_points为入口函数,使用pluggy插件中PluginManager类的load_setuptools_entrypoints方法加载,其中pytest11为入口点,这是官方定义的固定入口点,用于发现插件,参考

__init__.py:

import loggingfrom typing import Listimport pytestlogging.basicConfig(level=logging.INFO,                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',                    datefmt='%a, %d %b %Y %H:%M:%S',                    filename='report.log',                    filemode='w'                    )logger = logging.getLogger(__name__)def pytest_collection_modifyitems(session, config, items: List):    for item in items:        item.name = item.name.encode('utf-8').decode('unicode-escape')        item._nodeid = item.nodeid.encode('utf-8').decode('unicode-escape')    items.reverse()

2、打包

打包需要安装两个库:

  • wheel:pip install wheel
  • setuptools:pip install setuptools

进入包目录(pytest-encode)下,执行目录:

$ python setup.py sdist bdist_wheel

命令执行完成后,新生成如下文件:

pytest-encode/├── build/	├── bdist.win-amd64    └── lib		└── pytest_encode			└── __init__.py├── dist/	├── pytest-encode-0.0.1.tar.gz    └── pytest_encode-0.0.1-py3-none-any.whl		└──pytest_encode.egg-info/	├── dependency_links.txt	├── entry_points.txt	├── not-zip-safe	├── PKG-INFO	├── requires.txt	├── SOURCES.txt	    └── test_encode.py

pytest-encode-0.0.1.tar.gz为源码包,pytest_encode-0.0.1-py3-none-any.whl可以通过pip install命令安装

3、测试打包文件

我们新建一个Python虚拟环境,在新的虚拟环境下执行:

$ pip listPackage    Version---------- -------pip        21.0.1setuptools 54.2.0

只安装了两个包,下面安装刚才生成的包文件:

$ pip install pytest_encode-0.0.1-py3-none-any.whl

它会自动安装setup.py中定义的依赖包。

然后在新的虚拟环境中编写测试用例并执行:

import pytestfrom pytest_encode import logger@pytest.mark.parametrize("name",["张三","李四"])def test_name(name):    logger.info("测试编码")    print(name)
$ pytest -vs test_encode.py::test_name

结果(只截取部分):

test_hook.py::test_name[李四] 李四PASSEDtest_hook.py::test_name[张三] 张三PASSED

解码成功,并且生成了日志文件:

report.log:

Sat, 03 Apr 2021 20:21:45 test_encode.py[line:13] INFO 测试编码Sat, 03 Apr 2021 20:21:45 test_encode.py[line:13] INFO 测试编码

表明安装的包生效了。

4、发布包

使用Twine来上传包到PyPI,需要注册一个PyPI账号

然后安装twine:

$ pip install twine

上传:

$ twine upload --repository test-encode dist/*

上传过程中会让你输入前面注册的用户名和密码,上传成功后就可以在PyPI上查看

参考资料

  1. pytest-ordering:
  2. pluggy:
--THE END--

欢迎关注公众号:「测试开发小记」及时接收最新技术文章!

转载地址:http://axouz.baihongyu.com/

你可能感兴趣的文章
技术写作技巧分享:我是如何从写作小白成长为多平台优秀作者的?
查看>>
[Python学习笔记]组织文件
查看>>
基于Redo Log和Undo Log的MySQL崩溃恢复流程
查看>>
如何正确的在项目中接入微信JS-SDK
查看>>
纵览全局的框框——智慧搜索
查看>>
快服务流量之争:如何在快服务中占领一席之地
查看>>
【活动】直播揭秘<如何从0开发HarmonyOS硬件>
查看>>
Unity平台 | 快速集成华为性能管理服务
查看>>
对模拟器虚假设备识别能力提升15%!每日清理大师App集成系统完整性检测
查看>>
使用Power BI构建数据仓库与BI方案
查看>>
tep用户手册帮你从unittest过渡到pytest
查看>>
12张图打开JMeter体系结构全局视角
查看>>
Spring Boot 2.x基础教程:构建RESTful API与单元测试
查看>>
[UWP 自定义控件]了解模板化控件(1):基础知识
查看>>
UWP 自定义控件:了解模板化控件 系列文章
查看>>
[UWP]从头开始创建并发布一个番茄钟
查看>>
WinUI 3 Preview 3 发布了,再一次试试它的性能
查看>>
使用命令把SpringBoot项目打包成可运行的jar包(简洁,操作性强)
查看>>
List数组排序
查看>>
VMware vSphere 离线虚拟机安装 BIND 9
查看>>