核心内容摘要
如何让游戏音乐工具突破演奏效率瓶颈?探索ShawzinBot的MIDI转按键解决方案
测试学习记录仅供参考Pytest框架钩子函数钩子函数在 pytest 框架中是一个比较重要的概念在测试执行前、或测试执行过程中在前置功能里面做一些操作可以自定义钩子函数
钩子函数在Pytest框架中钩子函数是一种允许用户扩展或自定义测试执行过程的机制钩子函数允许用户在测试的不同阶段插入自定义的代码以实现特定的行为、操作或处理这种插入式的机制使得Pytest具有高度的灵活性和可扩展性Pytest的钩子函数遵循一定的命名规则通常以pytest_为前缀。
用户可以在测试代码中定义这些函数Pytest会在特定的时机调用它们比较常用的Pytest钩子函数有以下几种已在 pytest 框架中预定义的是只有去使用不能改函数名
pytest_configure(config)在配置阶段调用用于设置全局配置可以配置插件、自定义命令行选项等某些情况下会遇到偶尔
pytest_collection_modifyitems(config, items)在收集测试用例阶段调用允许修改、过滤或重新排序测试用例可以修改测试用例运行的排序规则有时候会用到
pytest_runtest_protocol(item, nextitem)在测试用例执行的不同阶段调用可以用于在测试前后执行特定操作用的不是很多基本上不使用
pytest_fixture_setup(fixturedef, request)在每个fixture设置之前调用可以用于自定义fixture的行为偶尔使用
pytest_fixture_post_finalizer(fixturedef, request)在每个fixture的最终清理阶段调用用于自定义fixture的清理操作若已经有在代码中自定义后置操作清理数据功能可以不再使用此钩子函数
pytest_runtest_makereport(item, call, report)在每个测试用例运行结束后调用允许自定义测试报告一般在conftest.py 中使用需要用到例如执行测试用例失败截图最终跑完之后只有测试失败的才会截图正常通过的不截图
pytest_terminal_summary(terminalreporter, exitstatus, config)在测试执行完毕后用于生成并显示最终的测试摘要信息到终端你可以实现自定义的测试报告汇总和显示。
例如可以在此钩子函数中计算测试覆盖率、输出额外的统计信息等等用的比较多在conftest.py文件使用所有测试用例执行完毕后自动收集测试结果
实际运用
一般钩子函数均需要写到 conftest.py 配置文件中
这里选择把钩子函数写到 项目根目录 testcase 软件包下 conftest.py 文件中自行选择pytest_runtest_makerepor
以 pytest_runtest_makerepor 为例
定义一个 def pytest_runtest_makereport() 函数因为是预定义的所以不能修改其函数名
pytest.hookimpl(hookwrapperTrue)添加 pytest 装饰器标记此函数是一个钩子函数
开始写代码实现写一个 yieldyield上面的是前置操作下面的是后置操作
coucome yield 自定义一个变量把结果给返回出去
通过这个 coucome 结果去调用 .get_result() 获取它的结果返回出去再打印查看# 钩子函数 pytest.hookimpl(hookwrapperTrue) def pytest_runtest_makereport(): coucome yield result coucome.get_result() print(result)
单独运行某条测试用例自行设置运行完之后可以发现每条测试用例有三个阶段whensetup 运行前whencall 运行过程中whenteardown 运行结束后能够在终端控制台打印出每条测试用例三个阶段“执行前、执行中、执行结束后”的信息结果通过 get_result() 方法去获取测试结果的报告对象这个报告对象包含测试用例的各种信息例如测试用例名称、阶段、执行结果- ------测试用例开始执行testcase------ TestReport testcase/other/test_file_upload.py::TestFilesUpload::test_file_upload_success whensetup outcomepassed ...... TestReport testcase/other/test_file_upload.py::TestFilesUpload::test_file_upload_success whencall outcomepassed PASSEDTestReport testcase/other/test_file_upload.py::TestFilesUpload::test_file_upload_success whenteardown outcomepassed ------测试用例执行完毕testcase------
拿到每个测试结果的报告对象对报告对象进一步操作
if result.when call: 判断一下这个结果的阶段是“执行中”阶段通过 result 去调用执行阶段的属性 when 来判断属于哪个阶段
xfail hasattr(result, wasxfail)判断测试结果里面是不是包含了自定义 wasxfail 属性返回结果hasattr(result, wasxfail)使用 hasattr 方法检测这个 result 对象有没有这个 wasxfail 属性其实就是用于标识测试用例是否预期失败的作用
if (result.skipped and xfail) or (result.failed and not xfail): 进一步判断这里因为要判断两个所以使用小括号 () 再判断 如果是一个跳过的 或者 是一个失败的 测试用例(result.skipped and xfail)判断测试用例是否被跳过并且是预期失败(result.failed and not xfail)判断测试用例是否执行失败并且不是预期失败
with测试用例执行失败时要做的一系列操作
with allure.step(测试用例失败截图): 引入 allure 模块使用 allure 报告中的 step 方法这个方法表示在测试报告中创建一个步骤测试步骤打印一句话自定义然后在步骤下面去创建一个截图
用例截图可以发现截图方法之前封装在公共方法里面的使用self.get_screenshot_as_png()截图方法需要先引入公共方法模块再传浏览器对象但是此时 conftest.py 这个文件中已经有初始化浏览器对象直接调用即可但是调用的话不能直接通过调用方法函数的方式它会找不到前置对象需要把浏览器初始化对象设置为全局属性自行选择合适的初始化浏览器对象在通过这个全局属性去调用driver.get_screenshot_as_png()截图方法allure.attach(self.get_screenshot_as_png(), 失败截图, attachment_typeallure.attachment_type.PNG)allure.attach(driver.get_screenshot_as_png(), 失败截图, attachment_typeallure.attachment_type.PNG)
失败测试用例可以做其他操作不一定非要截图例如失败重跑等等一系列其他的操作import pytest from selenium import webdriver from other.selenium_demo import driver from util_tools.logs_util.recordlog import logs from config.setting import browser_type, WAIT_TIME from pageObject.login_page.login_page import LoginPage import allure pytest.fixture(autouseTrue) def log_outputs(): logs.info(------测试用例开始执行testcase------) yield logs.info(------测试用例执行完毕testcase------) # pytest.fixture() # def get_driver(): # # 将driver设置为全局变量 # global driver # # # 初始化浏览器对象--字典 # browser_mapping { # Chrome: webdriver.Chrome, # Edge: webdriver.Edge, # Firefox: webdriver.Firefox # } # # # 判断配置文件 browser_type 变量值包含在 browser_mapping 字典里面 # if browser_type.capitalize() in browser_mapping: # driver browser_mapping.get(browser_type.capitalize())() # # # 设置一个全局的隐式等待时间 # driver.implicitly_wait(WAIT_TIME) # # 最大化浏览器窗口 # driver.maximize_window() # yield driver # driver.quit() def init_driver(): # 初始化浏览器对象--字典 browser_mapping { Chrome: webdriver.Chrome, Edge: webdriver.Edge, Firefox: webdriver.Firefox } # 判断配置文件 browser_type 变量值包含在 browser_mapping 字典里面 if browser_type.capitalize() in browser_mapping: return browser_mapping.get(browser_type.capitalize())() pytest.fixture(scopeclass) def get_driver(): driver init_driver() # 设置一个全局的隐式等待时间 driver.implicitly_wait(WAIT_TIME) # 最大化浏览器窗口 driver.maximize_window() yield driver driver.quit() # 登录状态的前置应用 pytest.fixture(scopeclass) def login_driver(get_driver): driver get_driver login_page LoginPage(driver) # 自行设置是否参数化 login_page.login(admin123,
return driver # 未登录状态的前置应用 pytest.fixture() def not_login_driver(): # 把浏览器初始化对象设置为全局属性driver global driver driver init_driver() # 设置一个全局的隐式等待时间 driver.implicitly_wait(WAIT_TIME) # 最大化浏览器窗口 driver.maximize_window() yield driver driver.quit() # 钩子函数对失败测试用例进行截图 pytest.hookimpl(hookwrapperTrue) def pytest_runtest_makereport(): coucome yield result coucome.get_result() # 判断这个阶段是“执行中”阶段 if result.when call: # xfail hasattr(result, wasxfail) # 再判断 如果是一个跳过的 或者 是一个失败的 测试用例 if (result.skipped and xfail) or (result.failed and not xfail): # 失败时要做的操作--用例截图 with allure.step(测试用例失败截图): # 设置浏览器初始化对象为全局属性driver通过driver去调用get_screenshot_as_png()方法 allure.attach(driver.get_screenshot_as_png(), 失败截图, attachment_typeallure.attachment_type.PNG)
可自行设置失败的测试用例去查看结果pytest_terminal_summary(terminalreporter, exitstatus, config)
这次在项目根目录下 conftest.py 文件中使用自行选择
简单示例如下把所有测试用例收集完之后才去统计相当于前后置操作中的后置处理def pytest_terminal_summary(terminalreporter, exitstatus, config): pytest预定义钩子函数用于自动收集测试用例执行结果 :param terminalreporter: 报告汇总内部使用的终端测试报告器对象用于输出内容 :param exitstatus: 返回码退出状态0-用例全部通过1-有用例失败2-还没执行在收集用例就失败了
4-其他报错5-收集到0条用例 :param config: pytest全局config配置对象 :return: # 调用里面的内置方法最后把收集统计到的测试用例总数量返回出去给一个变量把所有测试用例收集完之后才去统计 total terminalreporter._numcollected # 收集测试结果 passeds terminalreporter.stats print(f测试结果{passeds}) print(f统计测试用例总数{total}) print(f返回码退出状态{exitstatus}) print(f全局config对象{config})
自行运行测试用例查看控制台输出结果测试结果最外层是通过一个字典去存储所有的测试用例里面以键值对的展示无论是成功的还是失败的值均是以列表类型 统计测试用例总数XXXX 返回码退出状态0 全局config对象_pytest.config.Config object at 0x000001F5BC2175E
优化项目根目录下 conftest.py 文件可自行设置查看其他效果例如测试失败等待超时跳过等等# 导包 import time import pytest from util_tools.connectMysql import ConnectMysql from util_tools.logs_util.recordlog import logs # 添加后置应用--数据清理 pytest.fixture(scopesession, autouseTrue) def data_cleaning(): 测试结束后清理测试数据 # 初始化连接数据库对象 conn ConnectMysql() yield logs.info(正在清理测试数据...) # 删除语句注册成功用户--若有别的可以继续写 sql delete from ecs_users where user_nametest06 # 调用删除语句 conn.delete(sql) def pytest_terminal_summary(terminalreporter, exitstatus, config): pytest预定义钩子函数用于自动收集测试用例执行结果 :param terminalreporter: 报告汇总内部使用的终端测试报告器对象用于输出内容 :param exitstatus: 返回码退出状态0-用例全部通过1-有用例失败2-还没执行在收集用例就失败了
4-其他报错5-收集到0条用例 :param config: pytest全局config配置对象 :return: # 调用里面的内置方法最后把收集统计到的测试用例总数量返回出去给一个变量把所有测试用例收集完之后才去统计 total terminalreporter._numcollected # 通过数 passed len(terminalreporter.stats.get(passed, [])) # 失败数 failed len(terminalreporter.stats.get(failed, [])) # 错误数 error len(terminalreporter.stats.get(error, [])) # 跳过数 skipped len(terminalreporter.stats.get(skipped, [])) # 执行总时长--调用内置函数round() duration round(time.time() - terminalreporter._sessionstarttime,
# 定义一个字符串 summary f 自动化测试结果通过如下具体执行结果如下 测试用例总合计数{total} 测试执行通过数{passed} 测试执行失败数{failed} 错误数量{error} 跳过执行测试用例数量{skipped} 执行测试用例总耗时{duration}秒 # 打印字符串 print(summary)
运行主函数 run.py 文件执行所有的测试用例可以发现执行的稍慢是因为设置了 10 秒的隐式等待全局等待当某一个元素没有找到的情况下会去等 10 秒钟所以导致时间很慢可根据实际场景去设置小一点的隐式等待时间自动化测试结果通过如下具体执行结果如下 测试用例总合计数12 测试执行通过数12 测试执行失败数0 错误数量0 跳过执行测试用例数量0 执行测试用例总耗时
2
02秒 12 passed in
2
02s (0:03:
未完待续。
。
。