Day2 - Pytest (二)

断言

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比较的对象
relrel:相对容差(relative tolerance),默认为 1e-6
absabs:绝对容差(absolute tolerance),默认为 1e-12
nan_oknan_ok = true时,NaN等于其本身
  1. 无穷大只被认为等于自身,无论相对容差如何。

  2. NaN 不被认为等于任何东西,但是当 nan_ok 参数设置为 True 时 NaN 等于自身。

  3. 当指定 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 块时调用,无论是否发生异常都会执行,用于清理资源
使用 pytest 运行测试用例有多种方法: - **使用终端运行**:打开终端或命令提示符,导航到项目根目录(即包含 tests 文件夹的目录),输入 `pytest` 命令可运行所有测试 [^2]。 - **按模块、类或函数选择运行**:可以按照某个测试用例的模块、类或函数来选择要运行的测试用例。例如,`pytest -v test_pytest_markers.py::TestClass::test_method` 可运行指定模块中指定类的指定方法;`pytest -v test_pytest_markers.py::TestClass` 可运行指定模块中指定类的所有测试用例;还可以进行多种组合,如 `pytest -v test_pytest_markers.py::TestClass test_pytest_markers.py::test_send_http` [^1]。 - **使用关键字匹配运行**:使用 `-k` 进行关键字匹配来运行测试用例名字子串,如 `pytest -v -k http test_pytest_markers.py` [^1]。 - **通过 `pytest.main()` 函数运行**: - `pytest.main()`:无任何参数,会收集当前目录下所有的测试用例,相当于命令行模式下输入命令 `pytest`。 - `pytest.main(['test_sample.py'])`:一个参数,执行指定文件 `test_sample.py` 的所有测试用例。 - `pytest.main(['-q','test_sample.py'])`:个参数,参数 `-q` 表示安静模式,不输出环境信息 [^3]。 - **使用参数控制运行方式**: - `-s`:表示输出调试信息,包括 `print` 打印的信息。 - `-v`:显示更详细的信息,可与 `-s` 一起使用,如 `pytest -vs ./testcase/test_day1.py`。 - `-n`:支持多线程或者分布式运行测试用例,如 `pytest -vs ./testcase/test_day1.py -n 2`。 - `--reruns==number`:表示失败用例重跑,如 `pytest -vs ./testcase/test_day2.py --reruns 2`。 - `-x`:表示只要一个用例报错,那么测试停止运行。 - `--maxfail=2`:出现两个失败用例停止。 - `-k`:根据测试用例的部分字符串指定测试用例,如 `pytest -vs test_day2 -k "yang"` [^4]。 - **通过读取 `pytest.ini` 配置文件运行**:这是最主要运用的方式 [^4]。 ### 代码示例 ```python # 使用 pytest.main() 运行指定文件的测试用例 import pytest pytest.main(['test_sample.py']) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值