目錄
- 插件的加載方式
- 什么是hook
- Pytest有哪些hook函數(shù)
- 如何改寫hook函數(shù)
- 實戰(zhàn)打包晰韵、發(fā)布
Pytest插件加載方式
- 外部插件: pip install安裝的插件
- 本地插件:pytest自動模塊發(fā)現(xiàn)機制(conftest.py存放的)
- 內(nèi)置插件:代碼內(nèi)部的_pytest目錄加載
- 內(nèi)置插件位置:
External Libraries-> site-packages-> _pytest-> hookspec.py
- 內(nèi)置插件位置:
什么是hook
- 把大象裝冰箱 ,總共分幾步
- 打開冰箱門
- 把大象裝進(jìn)冰箱
- 關(guān)閉冰箱門
-
將以上幾步封裝成函數(shù)
- 如果想在“打開冰箱門”前面加一個“把燈打開”熟妓,再“把大象裝進(jìn)冰箱前”要“拿出一些零食”,就要重新改變代碼
-
hook就是將可能發(fā)生的接口/功能/操作預(yù)留出來栏尚,當(dāng)想進(jìn)行這些接口/功能/操作時加入到對應(yīng)位置即可起愈,即按照流程規(guī)范運行。
Pytest插件
1译仗、site-package/_pytest/hookspec.py
2抬虽、https://docs.pytest.org/en/latest/_modules/_pytest/hookspec.html
實戰(zhàn)一:編寫自己的插件——編碼
-
pytest_collection_modifyitems
收集上來的測試用例實現(xiàn)定制化功能 - 解決問題:
- 自定義用例的執(zhí)行順序
- 解決編碼問題(中文的測試用例名稱)
- 自動添加標(biāo)簽
- 含有中文的測試用例名稱,改寫編碼格式:
-
item.name = item.name.encode('utf-8').decode('unicode-escape')
:其中name為測試用例的名字 -
item._nodeid = item.nodeid.encode('utf-8').decode('unicode-escape')
:其中nodeid為測試用例的路徑 _pytest / nodes.py
-
test_chinese.py
代碼如下
import pytest
@pytest.mark.parametrize('name', ['哈利','赫敏'])
def test_chinese(name):
print(name)
- 運行結(jié)果:其中的用例名沒有用中文顯示,編碼格式為Unicode纵菌,不支持中文阐污。需要考慮修改測試用例名字和測試用例路徑的編碼格式
============================= test session starts =============================
rootdir: D:\Programs\DevOps\Python_Practice\Exercises\pytest_plugins
plugins: allure-pytest-2.8.40
collecting ... collected 2 items
test_chinese.py::test_chinese[\u54c8\u5229]
test_chinese.py::test_chinese[\u8d6b\u654f]
============================== 2 passed in 0.03s ==============================
Process finished with exit code 0
PASSED [ 50%]哈利
PASSED [100%]赫敏
- 修改pytest插件
- 在當(dāng)前目錄下新建文件
conftest.py
- 打開
External Libraries-> site-packages-> _pytest-> hookspec.py
文件,找到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.Session session: The pytest session object. :param _pytest.config.Config config: The pytest config object. :param List[pytest.Item] items: List of item objects. """
- 將方法復(fù)制到
conftest.py
文件下進(jìn)行改寫
def pytest_collection_modifyitems(ession, config, items): for item in items: item.name = item.name.encode('utf-8').decode('unicode-escape') item._nodeid = item.nodeid.encode('utf-8').decode('unicode-escape')
- 再次執(zhí)行
test_chinese.py
結(jié)果如下:發(fā)現(xiàn)用例名改為了中文
- 在當(dāng)前目錄下新建文件
rootdir: D:\Programs\DevOps\Python_Practice\Exercises\pytest_plugins
plugins: allure-pytest-2.8.40
collecting ... collected 2 items
test_chinese.py::test_chinese[哈利] PASSED [ 50%]哈利
test_chinese.py::test_chinese[赫敏] PASSED [100%]赫敏
============================== 2 passed in 0.02s ==============================
Process finished with exit code 0
實戰(zhàn)二:倒序執(zhí)行用例
- 由于
def pytest_collection_modifyitems(session, config, items:List):
中items為用例列表咱圆,所以可以對其進(jìn)行列表的方法笛辟,比如.reverse()
進(jìn)行倒序執(zhí)行 - 在
conftest.py
文件中加入items.reverse()
from typing import List
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')
# 修改用例執(zhí)行順序,其中 items 就是所有用例列表
items.reverse() # 倒序執(zhí)行
- 再次執(zhí)行
test_chinese.py
結(jié)果如下:發(fā)現(xiàn)用例執(zhí)行順序改變
rootdir: D:\Programs\DevOps\Python_Practice\Exercises\pytest_plugins
plugins: allure-pytest-2.8.40
collecting ... collected 2 items
test_chinese.py::test_chinese[赫敏] PASSED [ 50%]赫敏
test_chinese.py::test_chinese[哈利] PASSED [100%]哈利
============================== 2 passed in 0.02s ==============================
Process finished with exit code 0
實戰(zhàn)三:只執(zhí)行打了標(biāo)簽的測試用例
- 測試代碼如下序苏,其中有兩條用例名包含
login
:
import pytest
@pytest.mark.parametrize('name', ['哈利','赫敏'])
def test_chinese(name):
print(name)
def test_login():
print("login")
def test_login_fail():
print("login fail")
assert False
def test_search():
print("search")
- 修改
conftest.py
文件手幢,給login
打上標(biāo)簽
from typing import List
import pytest
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')
# 如果login在測試用例路徑中,則對其打標(biāo)簽
if "login" in item.nodeid:
item.add_marker(pytest.mark.login)
# 修改用例執(zhí)行順序忱详,其中 items 就是所有用例列表
items.reverse() # 倒序執(zhí)行
- 在Terminal中執(zhí)行
pytest -m login -vs
其中-m
為運行指定標(biāo)簽的用例围来,后面跟上標(biāo)簽名,-v
為打印詳細(xì)信息匈睁,-s
用于顯示測試函數(shù)中print()函數(shù)輸出监透。執(zhí)行結(jié)果如下,可以看到只執(zhí)行了login標(biāo)簽的用例
(venv) D:\Programs\DevOps\Python_Practice\Exercises\pytest_plugins>pytest -m login -vs
============================================================================================= test session starts =============================================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1 -- d:\programs\devops\python_practice\venv\scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\Programs\DevOps\Python_Practice\Exercises\pytest_plugins
plugins: allure-pytest-2.8.40
collected 5 items / 3 deselected / 2 selected
test_chinese.py::test_login_fail login fail
FAILED
test_chinese.py::test_login login
PASSED
================================================================================================== FAILURES ===================================================================================================
_______________________________________________________________________________________________ test_login_fail _______________________________________________________________________________________________
def test_login_fail():
print("login fail")
> assert False
E assert False
test_chinese.py:17: AssertionError
============================================================================================== warnings summary ===============================================================================================
conftest.py:17
D:\Programs\DevOps\Python_Practice\Exercises\pytest_plugins\conftest.py:17: PytestUnknownMarkWarning: Unknown pytest.mark.login - is this a typo? You can register custom marks to avoid this warning - for d
etails, see https://docs.pytest.org/en/stable/mark.html
item.add_marker(pytest.mark.login)
-- Docs: https://docs.pytest.org/en/stable/warnings.html
=========================================================================================== short test summary info ===========================================================================================
FAILED test_chinese.py::test_login_fail - assert False
============================================================================ 1 failed, 1 passed, 3 deselected, 1 warning in 0.23s =============================================================================
實戰(zhàn)四:添加命令行參數(shù)
def pytest_addoption(parser):
mygroup = parser.getgroup("hogwarts") #group將下面所有的option都展示在這個group下航唆。
mygroup.addoption("--env", #注冊一個命令行選項
default='test', #參數(shù)的默認(rèn)值
dest='env' ,#存儲的變量
help='set your run env' #幫助提示參數(shù)的描述信息
)
- 如何針對傳入的不同參數(shù)完成不同的邏輯處理?創(chuàng)建一fixture
@pytest.fixture(scope='session')
def cmdoption(request):
return request.config.getoption("--env", default='test')
- 在terminal中執(zhí)行
pytest --help
就能發(fā)現(xiàn)自定義的命令行參數(shù):
- 如果要獲取addoption中定義的命令行參數(shù)胀蛮,可以在
conftest.py
中定義fixture如下:
# 定義fixture從而獲取addoption里面函數(shù)
@pytest.fixture(scope='session')
def cmdoption(request):
env = request.config.getoption("--env", default='test')
if env == 'test':
print("這是測試環(huán)境")
elif env == 'dev':
print("這是開發(fā)環(huán)境")
- 新建一個測試用例如下,將在conftest中定義的fixture函數(shù)名傳過來
# 將在conftest中定義的fixture函數(shù)傳過來
def test_env(cmdoption):
print(cmdoption)
- 執(zhí)行結(jié)果如下:
test_chinese.py::test_env 這是測試環(huán)境
PASSED [100%]test
============================== 1 passed in 0.04s ==============================
Process finished with exit code 0
- 如果要修改環(huán)境為開發(fā)環(huán)境佛点,則在Terminal中執(zhí)行
pytest --env dev test_chinese.py::test_env -vs
醇滥,執(zhí)行結(jié)果如下:
rootdir: D:\Programs\DevOps\Python_Practice\Exercises\pytest_plugins
plugins: allure-pytest-2.8.40
collected 1 item
test_chinese.py::test_env 這是開發(fā)環(huán)境
dev
PASSED
實戰(zhàn)五:獲取環(huán)境數(shù)據(jù)
-
添加環(huán)境數(shù)據(jù)
- 其中dev下的
datas.yaml
內(nèi)容如下:
- 其中dev下的
env:
host: https://www.baidu.com
port: 443
- 其中test下的
datas.yaml
內(nèi)容如下:
env:
host: http://www.baidu.com
port: 80
- 修改
conftest.py
文件如下:主要修改cmdoption函數(shù)
from typing import List
import pytest
import yaml
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')
# 如果login在測試用例路徑中,則對其打標(biāo)簽
if "login" in item.nodeid:
item.add_marker(pytest.mark.login)
# 修改用例執(zhí)行順序超营,其中 items 就是所有用例列表
items.reverse() # 倒序執(zhí)行
# 添加一個命令行參數(shù)
def pytest_addoption(parser):
mygroup = parser.getgroup("hogwarts") #group將下面所有的option都展示在這個group下鸳玩。
mygroup.addoption("--env", #注冊一個命令行選項
default='test', #參數(shù)的默認(rèn)值
dest='env' ,#存儲的變量
help='set your run env' #幫助提示參數(shù)的描述信息
)
# 定義fixture從而獲取addoption里面函數(shù)
@pytest.fixture(scope='session')
def cmdoption(request):
env = request.config.getoption("--env", default='test')
if env == 'test':
print("這是測試環(huán)境")
datapath = "./datas/test/datas.yml"
elif env == 'dev':
print("這是開發(fā)環(huán)境")
datapath = "./datas/dev/datas.yml"
with open(datapath) as f:
datas = yaml.safe_load(f)
return env, datas
- 修改測試用例如下:
# 將在conftest中定義的fixture函數(shù)傳過來
def test_env(cmdoption):
env, datas = cmdoption
print(datas)
host = datas['env']['host']
port = datas['env']['port']
url = str(host) + ":" + str(port)
print(url)
- 測試結(jié)果:
plugins: allure-pytest-2.8.40
collected 1 item
test_chinese.py::test_env 這是開發(fā)環(huán)境
{'env': {'host': 'https://www.baidu.com', 'port': 443}}
https://www.baidu.com:443
PASSED
打包發(fā)布
- 打包必須要有代碼和setup.py 文件
-
setup.py 是一個構(gòu)建工具
打包需要兩個工具
- wheel, setuptools
- setup.py 文件
-
目錄結(jié)構(gòu)
- setup.py 文件:
from setuptools import setup
setup(
name='pytest_encode',
url='https://github.com/xxx/pytest-encode',
version='1.0',
author="loafer",
author_email='418974188@qq.com',
description='set your encoding and logger',
long_description='Show Chinese for your mark.parametrize(). Define logger variable for getting your log',
classifiers=[# 分類索引 ,pip 對所屬包的分類
'Framework :: Pytest',
'Programming Language :: Python',
'Topic :: Software Development :: Testing',
'Programming Language :: Python :: 3.8',
],
license='proprietary',
packages=['pytest_encode'],
keywords=[
'pytest', 'py.test', 'pytest_encode',
],
# 需要安裝的依賴
install_requires=[
'pytest'
],
# 入口模塊 或者入口函數(shù)
entry_points={
'pytest11': [
'pytest-encode = pytest_encode',
]
},
zip_safe=False
)
- pytest_encode 中的 _init_.py 文件
from typing import List
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')
# 修改用例執(zhí)行順序演闭,其中 items 就是所有用例列表
items.reverse() # 倒序執(zhí)行
- test_encode.py 文件
import pytest
@pytest.mark.parametrize('name', ['哈利','赫敏'])
def test_chinese(name):
print(name)
- 安裝wheel 工具:
pip install wheel
- 打包命令:
python setup.py sdist bdist_wheel
-
打包完后
- dist 中上面的是源碼包不跟,下面的是whl包,可以通過pip install 進(jìn)行安裝