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

(全文约3200字,阅读约需7分钟,建议先收藏后阅读。首发于公众号:测试开发研习社,欢迎关注)

图片

pytest 版本遵循 ( <major>.<minor>.<patch>) 语义控制。

昨天发布的 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 版本中,被处理为以下结果

<Session>  <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>

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

<Session>  <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.0
rootdir: D:\pytest_7.2.x
configfile: pytest.ini
collected 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.0
rootdir: D:\pytest_8.0.x
collected 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 开始,对默认值的返回进行了更加具体地处理规则:

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

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

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

2. 优化改进

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 中对于日志内容有多种处理方式:

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

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

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

  4. 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. 其他

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

如有兴趣,可以点击文章底部【查看原文】进行了解

3. BUG 修复

已经修复的 BUG 我不太关注

图片

4. 弃用规则

01. 测试用例不该有返回值

用例执行结果必须是 None,也就是不应该有返回值

三三三木,公众号:测试开发研习社pytest的内置插件盘点7:python

在此前的pytest版本中,如果用例返回非 None 的结果,会引发弃用警告

按照计划,弃用警告也会在 8.0 中引发错误、无法使用,并在 8.1 彻底清除。

但是此次 8.0 发布的版中,如果用例返回非 None 的结果,只会引发普通警告,不再引发弃用警告。

这也意味着,pytest 开发团队不再认为用例返回结果是一个错误

02. fixture 不应该添加标记

usefixture 是 pytest 的内置标记

三三三木,公众号:测试开发研习社pytest的内置插件盘点5:fixtures

我们知道,给测试用例在参数列表中加上 fixture 名,或者 usefixtures 标记中假设 fixture 名,会自动请求该 fixture

import pytest

@pytest.fixture()
def f():
    1/0


@pytest.mark.usefixtures('f')
def test_abc():
    pass

执行结果

图片

(双击可放大)

我们还知道,fixture 也可以请求 fixture,

所以给 fixture 在参数列表中加上 fixture 名,会自动请求该 fixture


import pytest


@pytest.fixture()
def ff():
    assert False


@pytest.fixture()
def f(f):
    1 / 0


@pytest.mark.usefixtures('f')
def test_abc():
    pass

执行结果

图片

(双击可放大)

但是请注意:给 fixture 加上 usefixtures 标记中 fixture 名,不会请求 fixture

因为标记只对用例有效,对 fixture 是无效的

图片

(双击可放大)

自 8.0 开始,再犯这样的错误,会引发弃用警告。

其实我不只见过这种给给 fixture 加标记的操作,还见过给用例加 fixture 的操作。。

图片

(双击可放大)

归根到底,是没有把 case 和 fixture 分清楚,强烈建议这种把操作也列入弃用

结语

老实说,我在刚看到对于 Pytest 8.0 正式发布时既兴奋又焦虑。

我的《pytest 源码剖析》才完成一半,就眼睁睁看着它从 7.0 更新到 7.2,又从 7.2 更新到 7.4,

现在又更新了个大版本到 8.0,真担心写的速度跟不上是它变得速度。

不过,在仔细研究了变更内容后,发现问题不大,预计 8.1 的时候可以写第二版本,就缩短距离了。

如果你 pytest 感兴趣的话欢迎点赞关注,希望本书能为你更深入了解 pytest 提供一些帮助

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值