首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

pytest 8.0 重磅发布!2条弃用规则,7项重大变更,多项优化改进

pytest 版本遵循 () 语义控制。

昨天发布的 pytest 8.0 是全新的major版本,

意味本次更新有重大变更,以及向后不兼容的修改!

1. 重大变更

01

移除历史包袱

一大批弃用警告升级为错误,从而无法继续使用,并将在8.1正式从代码中移除。

如果你还没有了解新版变化,手滑升级到了8.0后无法正常运行,可以通过屏蔽警告的方式,暂时过渡

[pytest]filterwarnings = ignore::pytest.PytestRemovedIn8Warning

注意,这是一个临时解决方案,8.1 之后这个方法也会失效

02

Python >=3.8

Python 3.7 生命周期在 2023 年 6 月 27 日已终止。

pytest 自本次更新之后,放弃了对 python 3.7 的兼容。

03

pluggy>=1.3.0

pluggy 1.3 引入了新的基于生成器的新式钩子包装器(hookwrapper)

在旧式的钩子包装器中,获取和修改钩子结果,使用了面向对象式的方式:

@pytest.hookimpl(hookwrapper=True)def pytest_pyfunc_call(pyfuncitem):

outcome = yield # <--------- 生成器方式接收对象

res = outcome.get_result() # <--- 【面向对象】方式获取旧结果

new_res = post_process_result(res)

outcome.force_result(new_res) # <--- 【面向对象】方式设置新结果

(左右可以滑动)

而在新式的钩子包装其中,获取和修改钩子结果,直接使用生成器自身的语法

@pytest.hookimpl(wrapper=True) # 使用新参数申明 新式包装器def pytest_pyfunc_call(pyfuncitem):

res = yield # <-------------- 生成器方式获取旧结果

new_res = post_process_result(res)

return new_res # <-------------- 生成器方式设置新结果

(左右可以滑动)

可以看到,新式写法简洁了很多,有更强烈的Python风格

04

区分包和目录

新增类:pytest.Directory表示目录

既有类:pytest.Package表示包

包和目录的概念得以加强,各司其职

假设有以下目录关系

myroot/ pytest.ini top/ ├── aaa │ └── test_aaa.py ├── test_a.py ├── test_b │ ├── __init__.py │ └── test_b.py ├── test_c.py └── zzz ├── __init__.py └── test_zzz.py

在此前的 pytest 版本中,被处理为以下结果

<Module top/test_a.py> <Function test_it> <Module top/test_c.py> <Function test_it> <Module top/aaa/test_aaa.py> <Function test_it> <Package test_b> <Module test_b.py> <Function test_it> <Package zzz> <Module test_zzz.py> <Function test_it>

本次更新之后,会被处理为这样

<Dir myroot> <Dir top> <Dir aaa> <Module test_aaa.py> <Function test_it> <Module test_a.py> <Function test_it> <Package test_b> <Module test_b.py> <Function test_it> <Module test_c.py> <Function test_it> <Package zzz> <Module test_zzz.py> <Function test_it>

05

调整用例收集顺序

此前,用例收集顺序是先文件、后目录

此次更新中,用例收集按照字母顺序进行

这一点请macOS用户特别关注,此前因操作系统的差异,macOS中用例执行顺序和Windows中有细微差异。

此次更新可能回到结果造成影响

具体例子可回顾上一小节中收集结果

06

断言警告

pytest 提供了一个对警告进行断言的方式

def test_is_user_waring(): # 出现指定类型警告则测试通过,否则失败 with pytest.warns(UserWarning): warnings.warn(f"这是一条用户警告", UserWarning) warnings.warn(f"这是一条语法警告", SyntaxWarning)

(左右可以滑动)

在此前的版本中执行结果如下(无事发生...)

===================== test session starts ==================platform win32 -- Python 3.12.0, pytest-7.4.0, pluggy-1.0.0rootdir: D:\pytest_7.2.xconfigfile: pytest.inicollected 1 item

test_show_warnings.py . [100%]

===================== 1 passed in 0.01s =====================

(左右可以滑动)

自8.0开始,warns只会捕获它所断言的警告类型,至于其他警告,则会重新抛出,执行结果如下

===================== test session starts ==================platform win32 -- Python 3.12.0, pytest-8.0.0, pluggy-1.4.0rootdir: D:\pytest_8.0.xcollected 1 item

test_show_warnings.py .    [100%]

===================== warnings summary ===================== test_show_warnings.py::test_is_user_waring D:\pytest_8.0.x\test_show_warnings.py:10: SyntaxWarning: 这是一条语法警告 warnings.warn(f"这是一条语法警告", SyntaxWarning)

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html============== 1 passed, 1 warning in 0.01s =================

(左右可以滑动)

07

净化 ini 配置的默认值

过去,对于未设置的 ini 配置,会根据类型返回空列表或空字符串,

于是出现了一个 bug:如果设置默认值 None ,会像未设置默认值一样,返回空列表

自8.0开始,对默认值的返回进行了更加具体地处理规则:

如果设置了默认值,返回默认值(哪怕默认值是 None)

没有设置默认值但申明了类型,根据类型返回空列表、空字符串或布尔值 False

没有设置默认,也没有申明类型,返回空字符串

二、 优化改进

01

改进差异显示(diff)

建议安装pygments,增加色彩突出

对于同一个测试用例

def test_diff_list(): a = list('123') b = list('123345')

pytest 7 的执行结果如下

(双击可放大)

更新至8.0后,差异显示更加直观

(双击可放大)

不仅告诉我们错了,还是告诉我们哪里错了;不仅告诉我们哪里错了,还告诉我们为什么错了,具体是哪个字符不对...

还有比这更贴心的测试框架吗?

02

单独控制断言详细程度

如果想要像上一小节中那样显示断言的详细信息,需要添加命令行参数-vv

pytest --vv

需要注意的是:-vv不仅让断言信息更加详细,

也让整个终端输出更加详细,比如会更详细地显示版本信息、用例名、用例执行结果等。

如果只希望断言详细,而不需要其他信息变冗长呢?

8.0新增了一个 ini 配置项目verbosity_assertions,可单独对断言详细程度进行控制

[pytest]verbosity_assertions = 2

03

新式钩子包装器

更加纯粹的,符合生成器风格的钩子包装器,

详见前文 1.3

04

优化日志配置

这个bug 我在前几天读源码的时候发现了,本来准备提交补丁刷个贡献值,

不过已经被人在 9 月份就抢了先。。。

就说说 BUG 的原因:

首先,在 pytest 中对于日志内容有多种处理方式:

caplog_handler:记录到 fixture 中,供用例使用

report_handler:记录到 report 中,供测试报告使用

log_file_handler:记录到文件中,形成日志文件

log_cli_handler:记录到终端中,在命令行输出

然后 pytest 配置文件中有 3 种对日志的配置选项

log_cli_*:作用于终端

log_file_*:作用于文件

log_*:作用于全局

直观上来说,如果log_cli_*或log_file_*没有配置的话,

应该读取log_*中的内容

或者,如果log_cli_*或log_file_*完全相同的话,不必重复配置 2 次,

应该直接对log_*进行配置

没错,代码中也是怎么写的。。。

但是!但是!log_cli_*或log_file_*居然有默!认!值!

就算你真的没有对它们配置,它们也会读取到默认值而不是log_*中的内容,

导致相同的内容必须配置3次才能正常工作

在此次更新中,修复了这个BUG,统一和简化了日志的配置

05

其他

还有一些改进,以文档和类型申明为主,

  • 发表于:
  • 原文链接https://page.om.qq.com/page/Ov-OpQRFm1srJjvrrwsxQXvA0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券