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() 对异常进行捕获,甚至断言。
喜时之言,多失信;怒时之言,多失体