pytest的AST 断言重写

"AST 断言重写"

你平时写 assert result == expected,在 Pytest 底层,这行代码在运行前就被语法树(AST)重写了。

Pytest 把它变成了一段极其复杂的代码,不仅判断结果对不对,还会把 result 和 expected 的每一个属性、每一个变量的值都打印出来。这就是为什么 Pytest 报错时,能给你提供那么丰富、那么详细的上下文信息。

一个极其重要的"避坑"指南

既然你学到了这里,我必须告诉你一个 AST 重写机制的致命弱点:

如果你在 assert 后面加了自定义的提示信息,AST 重写就会立刻失效!

python 复制代码
# ❌ 错误写法:加了逗号后面的提示信息,Pytest 的丰富报错就没了!
assert a == b, "a 和 b 不相等"

# ✅ 正确写法:让 Pytest 尽情发挥 AST 重写的能力
assert a == b 

为什么? 因为 Pytest 的 AST 重写机制在解析语法树时,如果发现 assert 节点带了自定义的 Message 参数,为了尊重开发者的原始意图,它会放弃重写,退化为原生 Python 的 raise AssertionError("a 和 b 不相等")。

真正想问的底层逻辑是:Pytest 的底层源码,究竟是如何"看懂"并识别出 assert a == b, "提示信息" 这种带逗号写法的?

这就要回到我们上一轮聊到的核心概念:AST(抽象语法树)。

在 Python 的 AST 语法树眼里,assert 语句并不是简单的文本,而是一个结构化的"节点(Node)"。这个节点有两个非常关键的属性(插槽):

test:存放断言的条件(比如 a == b)。

msg:存放自定义的提示信息(比如 "a 和 b 不相等")。

当你在代码里写下这两种不同的写法时,Python 编译器生成的 AST 节点是完全不同的:

写法一:不带提示信息

python 复制代码
assert a == b

生成的 AST 节点:
test = a == b
msg = None (空的)

写法二:带了提示信息(有逗号)

python 复制代码
assert a == b, "a 和 b 不相等"

生成的 AST 节点:
test = a == b
msg = "a 和 b 不相等" (有具体的字符串值)

Pytest 源码是如何"看懂"的?

Pytest 在重写代码时,会在源码里写一个极其明确的 if...else... 判断逻辑(你可以去 Pytest 源码 src/_pytest/assertion/rewrite.py 的 visit_Assert 方法里亲眼看到它)。

它的底层伪代码逻辑大概是这样的:

python 复制代码
def visit_Assert(self, node):
    # Pytest 在这里"看" AST 节点的 msg 属性
    if node.msg is not None:
        # 发现 msg 不是空的!说明用户自己写了自定义提示信息。
        # 策略:放弃重写,直接保留原样,退化为原生 Python 的 assert 行为。
        return node 
    else:
        # 发现 msg 是空的!说明用户只写了 assert a == b。
        # 策略:启动 AST 魔法重写,把这段代码变成包含丰富调试信息的复杂代码!
        return self.rewrite_assert(node)

总结

所以,计算机(Pytest)并不是像人类一样去"阅读"你的注释或代码习惯。它是通过解析 AST 语法树的结构,极其精确地判断 msg 这个属性到底有没有值。

如果 msg 有值,它就认为:"哦,用户有自己的报错需求,我不插手。"

如果 msg 没值,它就认为:"太好了,用户需要我的高级调试功能,我来重写它!"

相关推荐
2601_9622845018 小时前
安卓APP UI自动化测试:Python + UiAutomator2 + pytest + pytest-html
python·pytest·uiautomator2·ui自动化测试·安卓app
2601_9623008118 小时前
基于pytest的接口自动化测试:使用requests模块的实战项目与接口关联方法详解
接口自动化测试·测试用例·pytest·接口关联·requests模块
2601_9623008119 小时前
Python + Requests + Pytest + Excel + Allure:接口自动化测试项目实战
python·excel·pytest·allure·requests
11路没有终点2 天前
Pytest 安装、版本与第一个测试用例
测试用例·pytest
11路没有终点3 天前
pytest 参数化与 Mark 标记
pytest
2601_962296283 天前
五种Python自动化测试框架汇总,附学习方法
自动化测试·pytest·unittest·robotframework·python框架
11路没有终点4 天前
pytest Fixture 进阶:conftest 与依赖注入
pytest
2601_962387827 天前
web自动化测试实战教程【selenium/unittest/pytest】【共193课
selenium·pytest·devops·web自动化测试·unittest
weixin_440730507 天前
使用pytest中方法控制执行步骤(test_begin.py、test_end.py,@pytest.mark.run(order=1))
开发语言·python·pytest
2601_962297257 天前
Python、Pytest、Allure、Selenium和Jenkins实现自动化测试集成实例
python·selenium·jenkins·pytest·allure