视频来源:B站《冒死上传!pytest接口自动化测试框架(基础理论到项目实战及二次开发)教学视频【软件测试】》
一边学习一边整理老师的课程内容及试验笔记,并与大家分享,侵权即删,谢谢支持!
一、HOOK函数详解
HOOK函数的定义
HOOK=钩子
钩子函数:
1、是个函数,在系统消息触发时别系统调用
2、不是用户自己触发的
3、使用时直接编写函数体
4、钩子函数的名称是确定,当系统消息触发,自动会调用
比如:pytest_runntest_makereport
插件与hook函数关系:
插件就是用1个或者多个hook函数,也就是钩子函数构成的。如果想要编写新的插件,或者是仅仅改进现有的插件,都必须通过这个hook函数进行。所以想掌握pytest插件二次开发,必须搞定hook函数
【HOOK函数简单实践-修改pytest-html报告】
conftest.py
from datetime import datetime
from py.xml import html
import pytest
"""
使用测试函数docstring添加描述(Description)列,添加可排序时间(Time)列
并删除链接Link列
"""
# 添加表头
def pytest_html_results_table_header(cells):
# 添加Description列
cells.insert(2, html.th("Description"))
# 添加Time列
cells.insert(1, html.th("Time", class_="sortable time", col="time"))
cells.pop() # 删除最后link列的内容
# 添加表格内容
def pytest_html_results_table_row(report, cells):
cells.insert(2, html.td(report.description))
cells.insert(1, html.td(datetime.utcnow(), class_="col-time"))
cells.pop()
# 获取用例的注释信息
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
report.description = str(item.function.__doc__)
main.py
import pytest
if __name__ == '__main__':
pytest.main(['-s', '-v', '--capture=sys', '--html=./report/report.html'])
test_case01.py
import pytest
def test_case01():
"""
用例1标题
用例1步骤
用例1预期结果
"""
assert 1==1
def test_case02():
"""
用例2标题
用例2步骤
用例2预期结果
"""
assert 1==2
def test_case03():
"""
用例3标题
用例3步骤
用例3预期结果
"""
assert 1==3
运行结果:
【装饰器@pytest.hookimple(hookwrapper=True)】
@pytest.hookimple(hookwrapper=True)装饰的钩子函数,有以下两个作用:
(1)可以获取到测试用例不同执行阶段的结果(setup all teardown)
(2)可以获取钩子方法的调用结果(yield返回一个result对象)和调用结果的测试报告(返回一个report对象)
【HOOK函数排序/调用示例】
官方解释:https://docs.pytest.org/en/latest/how-to/writing_hook_functions.html
对于任何给定的钩子函数规格,可能存在多个实现,因为我们通常将钩子函数执行视为1:N的函数调用,其中N是已注册函数的数量。有一些方法可以影响钩子函数实现是在其他之前还是之后,即在N-sized函数列表中的位置:
执行顺序:
以上使用tryfirst,trylast,以及结合hookwrapper=True的示例,它会影响彼此之间hookwrappers的排序
Pytest Hook钩子函数完整API
官方:https://docs.pytest.org/en/latest/reference/reference.html?highlight=hooks#hooks
Hooks函数terminal打印测试结果(pytest_report_teststatus)
Hooks函数之统计测试结果(pytest_terminal_summary)
Hooks函数改变用例执行顺序(pytest_collection_modifyitems)
Hooks函数自定义添加命令行参数
。。。
二、插件操作文档详解
pytest-html官网API:
https://pytest-html.readthedocs.io/en/latest/user_guide.html#creating-a-self-contained-report
添加额外内容
三、使用hook函数进行插件二次开发
【错误截图插入HTML格式测试报告】
钩子函数:pytest_runtest_makereport()
主要功能:判断每条用例运行情况
开发思路:
1)将失败或错误的用例截图到本地目录中
当测试用例错误或失败后会调用函数进行截图,并且将测试用例“文件名+类名+方法名”作为截图的名称,保存到image/目录中
2)将截图插入到HTML格式的测试报告中
提问:pytest-html会生成一张HTML格式的测试报告,那么如何将截图插入到HTML格式的测试报告中?
添加标签,通过src属性指定图片的路径
衍生问题:ids中文导致控制台输出乱码
实现本地截图功能
截图方法介绍:
conftest.py
import os
from selenium import webdriver
import pytest
from hook_practice.config import RunConfig
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
pytest_html = item.config.pluginmanager.getplugin("html")
outcome = yield
report = outcome.get_result()
extra = getattr(report, "extra", [])
# 只获取用例运行时的结果
if report.when == "call":
# # always add url to report
# extra.append((pytest_html.extras.url("http://www/.example.com/")))
# 用例失败
xfail = hasattr(report, "wasxfail")
if (report.skipped and xfail) or (report.failed and not xfail):
# 将测试用例的“文件名+类名+方法名”作为截图的名称
# 获取nodeid,并处理::,替换成_
case_path = report.nodeid.replace("::", "_") + ".png"
# 还要处理减号-,叠加参数化ids,需要做判断处理
if "[" in case_path:
# 通过分隔符取第一个元素,避免出现-号,参数化叠加使用会出现
case_name = case_path.split("-")[0] + "].png"
else:
case_name = case_path
# 定义方法,实现本地截图
capture_screenshots(case_name)
# case_name,不能有/
img_path = "image/" + case_name.split("/")[-1]
# 如果存在img_path,向pytest_html中添加截图网页代码
if img_path:
html = '
config.py
import os
# 获取代码目录的绝对路径
PRO_PATH = os.path.dirname(os.path.abspath(__file__))
class RunConfig:
"""
测试配置
"""
# 运行测试用例的目录或文件
case_path = os.path.join(PRO_PATH, "test_case", "test_baidu.py")
# 配置浏览器驱动类型(Chrome/Firefox)
driver_type = "chrome"
# 配置运行的URL
url = "http://www.baidu.com"
# 失败重跑次数
rerun = "1"
# 当达到失败最大数,停止运行
max_fail = "5"
# 测试报告存放的根目录
REPORT_DIR = PRO_PATH + "/test_report/"
# 浏览器驱动
driver = None
# 新报告路径
NEW_REPORT = None
main.run.py
import os
import time
import pytest
from hook_practice.config import RunConfig
def init_env(new_report):
"""
初始化报告目录
"""
os.mkdir(new_report)
os.mkdir(new_report + "/image")
def run():
# 获取时间,按年月日时分秒排列
now_time = time.strftime("%Y_%m_%d_%H_%M_%S")
# 根目录+时间成为每次生成报告的唯一目录
RunConfig.NEW_REPORT = os.path.join(RunConfig.REPORT_DIR, now_time)
init_env(RunConfig.NEW_REPORT)
# 定义测试报告的绝对路径
html_report = os.path.join(RunConfig.NEW_REPORT, "report.html")
# 定义备用的xml格式报告的绝对路径
xml_report = os.path.join(RunConfig.NEW_REPORT, "junit-xml.html")
# 指定目录/文件执行用例
pytest.main(["-s", "-v", RunConfig.case_path,
"--html=" + html_report,
"--junit-xml=" + xml_report,
# 捕获错误日志输出信息到测试报告
"--capture=sys",
# 设置最大失败次数
"--maxfail", RunConfig.max_fail,
# 设置失败重跑次数
"--reruns", RunConfig.rerun])
if __name__ == '__main__':
run()
test_case/test_baidu.py
from time import sleep
# 使用在conftest中初始好了的driver
def test_baidu_case01(browser):
driver = browser
driver.get("http://www.baidu.com")
sleep(1)
driver.find_element_by_id('kw').send_keys("狗狗币")
sleep(1)
driver.find_element_by_id('su').click()
assert driver.title == "11111"
留言与评论(共有 0 条评论) “” |