断言
assert 标准断言
def f():
return 3
def test_function():
assert f() == 4
在示例中assert将断言一个函数的返回值,断言失败将看到函数调用的返回值
pytest 支持显示最常见的子表达式的值,包括调用、属性、比较以及二进制和一元运算符
pytest.approx() 近似性断言
import pytest
import numpy as np
def test_floats():
assert (0.1 + 0.2) == pytest.approx(0.3)
def test_arrays():
a = np.array([1.0, 2.0, 3.0])
b = np.array([0.9999, 2.0001, 3.0])
assert a == pytest.approx(b)
**函数参数:**approx(expected, rel=None, abs=None, nan_ok=False)
**作用:**断言两个数字(或两个有序数字序列)在一定容差范围内彼此相等,浮点算术:问题与限制。
| 参数 | 解释 |
|---|---|
| expected | 比较的对象 |
| rel | rel:相对容差(relative tolerance),默认为 1e-6 |
| abs | abs:绝对容差(absolute tolerance),默认为 1e-12 |
| nan_ok | nan_ok = true时,NaN等于其本身 |
-
无穷大只被认为等于自身,无论相对容差如何。
-
NaN 不被认为等于任何东西,但是当
nan_ok参数设置为 True 时 NaN 等于自身。 -
当指定 abs 后将完全不考虑相对误差是否满足,如果 rel和abs同时指定的话只要有一个满足条件则视为通过
pyhtest.raise() 异常预期的断言
import pytest
def test_recursion_depth():
with pytest.raises(RuntimeError) as excinfo:
def f():
f()
f()
assert "maximum recursion" in str(excinfo.value)
当excinfo.value(异常的实例对象)中出现 maximum recursion 时视为pass
excinfo 是一个 [ExceptionInfo](https://pytest.cn/en/stable/reference/reference.html#pytest.ExceptionInfo) 实例,它是围绕实际抛出的异常的包装器。主要关注的属性是 .type异常的 类型(类)、.value异常的 实例对象,即实际被抛出的那个异常对象 和 .traceback异常发生时的 回溯信息(Traceback),记录了异常从抛出到被捕获的完整调用栈。
def test_foo_not_implemented():
def foo():
raise NotImplementedError
with pytest.raises(RuntimeError) as excinfo:
foo()
assert excinfo.type is RuntimeError
只要foo中有异常则断言为pass
pyhtest.RaiseGroup() 异常预期组的断言
异常组: [BaseExceptionGroup](https://docs.pythonlang.cn/3/library/exceptions.html#BaseExceptionGroup) 或 [ExceptionGroup](https://docs.pythonlang.cn/3/library/exceptions.html#ExceptionGroup)
RaiseGroup()用来精确断言 ExceptionGroup 中包含哪些异常,包括嵌套结构、异常类型、消息等。
| 参数 | 解释 |
|---|---|
| *args | 指定期望在 ExceptionGroup 中出现的异常项。可以是:1. 异常类(如 ValueError 2. RaisesExc(…)(带匹配条件的异常)3. 嵌套的 RaisesGroup(…)(用于匹配子组) |
| match | 对整个 ExceptionGroup 的字符串表示(含 notes)进行正则匹配。使用 re.search()。 |
| check | 自定义校验函数,接收捕获到的异常组实例,返回 True 表示通过。 |
| allow_unwrapped | 若为 True,允许被测代码直接抛出单个异常(而非 ExceptionGroup),仅当 args 中只含一个非 RaisesGroup 异常时可用。 |
| flatten_subgroups | 若为 True,在匹配前自动“打平”嵌套的子异常组,将所有叶子异常提升到顶层进行匹配。 |
import pytest
def test_basic_exception_group():
with pytest.raises(ExceptionGroup): # 可选:先确认是 ExceptionGroup
with pytest.RaisesGroup(ValueError, TypeError):
raise ExceptionGroup("Input errors", [ValueError("bad value"), TypeError("wrong type")])
# 验证:异常组中恰好包含一个 ValueError 和一个 TypeError,无其他异常。
def test_match_group_message_and_subexception():
with pytest.RaisesGroup(
ValueError,
pytest.RaisesExc(TypeError, match=r"expected int, got str"),
match=r"^Validation failed$"
):
raise ExceptionGroup(
"Validation failed",
[
ValueError("missing field"),
TypeError("expected int, got str")
]
)
# 验证:
# 整个异常组消息为 "Validation failed"
# 子异常中有一个 TypeError,其消息匹配正则
# 还有一个普通的 ValueError
def test_nested_exception_groups():
with pytest.RaisesGroup(
pytest.RaisesGroup(KeyboardInterrupt),
ValueError
):
raise ExceptionGroup(
"Outer group",
[
ExceptionGroup("Inner group", [KeyboardInterrupt()]),
ValueError("top-level error")
]
)
# 验证:
# 顶层有两个子异常:一个 ExceptionGroup(内含 KeyboardInterrupt)和一个 ValueError
# 结构必须完全匹配(未使用 flatten_subgroups)
def test_flatten_nested_groups():
with pytest.RaisesGroup(
ValueError,
TypeError,
flatten_subgroups=True
):
raise ExceptionGroup(
"Level 1",
[
ExceptionGroup("Level 2", [ValueError()]),
ExceptionGroup("Level 2b", [
ExceptionGroup("Level 3", [TypeError()])
])
]
)
# 验证:无论嵌套多深,只要最终叶子异常是 ValueError 和 TypeError 各一个,就通过。
def process_data(x):
if x < 0:
raise ValueError("x must be non-negative")
elif x == 0:
raise ExceptionGroup("Zero input", [ValueError("zero not allowed")])
else:
return x * 2
def test_allow_unwrapped_for_flexible_api():
# 情况1:直接抛出单个异常
with pytest.RaisesGroup(ValueError, allow_unwrapped=True):
process_data(-1)
# 情况2:抛出异常组
with pytest.RaisesGroup(ValueError, allow_unwrapped=True):
process_data(0)
# 验证:同一个测试逻辑能同时处理“直接抛异常”和“抛异常组”两种情况。
def test_custom_check_with_cause():
with pytest.RaisesGroup(
OSError,
match="File operation failed",
check=lambda eg: (
len(eg.exceptions) == 1 and
isinstance(eg.__cause__, PermissionError)
)
):
try:
raise PermissionError("denied")
except PermissionError as e:
raise ExceptionGroup("File operation failed", [OSError("read failed")]) from e
# 验证:
# 异常组消息匹配
# 内部只有一个 OSError
# 整个异常组的 __cause__ 是 PermissionError
raise:主动抛出一个异常,用于表示程序遇到了错误、非法状态或需要中断执行的情况。
with:**用于简化资源管理的控制流语句,**确保某个代码块执行前后,自动完成“进入”和“退出”操作,要求后面的函数必须实现__enter__(self) 方法和__exit__(self, exc_type, exc_val, exc_tb)
_enter__(self) | 进入 with 块时调用,通常返回资源(如文件对象) |
|---|---|
__exit__(self, exc_type, exc_val, exc_tb) | 退出 with 块时调用,无论是否发生异常都会执行,用于清理资源 |
1501

被折叠的 条评论
为什么被折叠?



