pytest扫盲14--pytest.raises()函数文档及在参数化中处理异常

pytest.raises() 函数文档如下:

def raises(  # noqa: F811
    expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]],
    *args: Any,
    **kwargs: Any
) -> Union["RaisesContext[_E]", _pytest._code.ExceptionInfo[_E]]:
    r"""
    Assert that a code block/function call raises ``expected_exception``
    or raise a failure exception otherwise.

    :kwparam match: if specified, a string containing a regular expression,
        or a regular expression object, that is tested against the string
        representation of the exception using ``re.search``. To match a literal
        string that may contain `special characters`__, the pattern can
        first be escaped with ``re.escape``.

        (This is only used when ``pytest.raises`` is used as a context manager,
        and passed through to the function otherwise.
        When using ``pytest.raises`` as a function, you can use:
        ``pytest.raises(Exc, func, match="passed on").match("my pattern")``.)  

        __ https://docs.python.org/3/library/re.html#regular-expression-syntax  # 正则表达式官方文档

    .. currentmodule:: _pytest._code

    Use ``pytest.raises`` as a context manager, which will capture the exception of the given
    type::  # 使用 pytest.raises 作为上下文管理器,捕获给定异常类型.

        >>> with raises(ZeroDivisionError):
        ...    1/0

    If the code block does not raise the expected exception (``ZeroDivisionError`` in the example
    above), or no exception at all, the check will fail instead.  # 代码中未发生异常,会检查失败

    You can also use the keyword argument ``match`` to assert that the
    exception matches a text or regex::  # 使用关键字或正则表达式匹配异常文本

        >>> with raises(ValueError, match='must be 0 or None'):
        ...     raise ValueError("value must be 0 or None")

        >>> with raises(ValueError, match=r'must be d+$'):
        ...     raise ValueError("value must be 42")

    The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the
    details of the captured exception::    # 上下文管理器会产生一个对象 ExceptionInfo,用于检查异常的详细信息  
  
        >>> with raises(ValueError) as exc_info:
        ...     raise ValueError("value must be 42")
        >>> assert exc_info.type is ValueError  # 异常类型,异常类型只能是标准类型  .type
        >>> assert exc_info.value.args[0] == "value must be 42"  # 异常值,元祖  .value.arg[index]

    .. note::

       When using ``pytest.raises`` as a context manager, it's worthwhile to
       note that normal context manager rules apply and that the exception
       raised *must* be the final line in the scope of the context manager.
       Lines of code after that, within the scope of the context manager will
       not be executed. For example::  # 这里是关于 pytest.raises 作用范围的例子,只能在with范围内引发异常,之后的代码不会只想。要使用必须跳出 with 作用范围

           >>> value = 15
           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...     assert exc_info.type is ValueError  # this will not execute

       Instead, the following approach must be taken (note the difference in
       scope)::

           >>> with raises(ValueError) as exc_info:
           ...     if value > 10:
           ...         raise ValueError("value must be <= 10")
           ...
           >>> assert exc_info.type is ValueError

    **Using with** ``pytest.mark.parametrize``  # 使用 pytest.mark.parametrize 进行参数化,使某些运行引发异常,其他运行正常执行

    When using :ref:`pytest.mark.parametrize ref`
    it is possible to parametrize tests such that
    some runs raise an exception and others do not.

    See :ref:`parametrizing_conditional_raising` for an example.

    **Legacy form**  # 这里是直接使用 raises() 调用参数的一种用法(不推荐)

    It is possible to specify a callable by passing a to-be-called lambda::

        >>> raises(ZeroDivisionError, lambda: 1/0)
        <ExceptionInfo ...>

    or you can specify an arbitrary callable with arguments::

        >>> def f(x): return 1/x
        ...
        >>> raises(ZeroDivisionError, f, 0)
        <ExceptionInfo ...>
        >>> raises(ZeroDivisionError, f, x=0)
        <ExceptionInfo ...>

    The form above is fully supported but discouraged for new code because the
    context manager form is regarded as more readable and less error-prone.  # 关于碎片整理的信息

    .. note::
        Similar to caught exception objects in Python, explicitly clearing
        local references to returned ``ExceptionInfo`` objects can
        help the Python interpreter speed up its garbage collection.

        Clearing those references breaks a reference cycle
        (``ExceptionInfo`` --> caught exception --> frame stack raising
        the exception --> current frame stack --> local variables -->
        ``ExceptionInfo``) which makes Python keep all objects referenced
        from that cycle (including all local variables in the current
        frame) alive until the next cyclic garbage collection run.
        More detailed information can be found in the official Python
        documentation for :ref:`the try statement <python:try>`.
    """

利用raise()函数文档给出的例子再优化下 parametrize 参数化的例子(注意标红的部分,在参数化数据中直接处理异常):

更多关于 parametrize 的用法见官方文档:  https://docs.pytest.org/en/latest/example/parametrize.html

# File  : test_demo_14.py
# IDE   : PyCharm

import pytest

def division(a, b):
    return int(a / b)

@pytest.mark.parametrize('a, b, c', [(4, 2, 2), (0, 2, 0), (1, 0, pytest.raises(ZeroDivisionError)), (6, 8, 0)], ids=['整除', '被除数为0', '除数为0', '非整除'])
def test_2(a, b, c):
    '''使用 pytest.raises 接收异常'''
    if b == 0:
        with c:
            assert division(a, b) is not None
    else:
        assert division(a, b) == c

执行后:

此例子旨在说明,在进行参数化时,对于预期会失败的用例,在构造测试数据时,也可以采用 python.raise() 对异常进行捕获,甚至断言。

原文地址:https://www.cnblogs.com/xiaohuboke/p/13540319.html