如何正确地断言在pytest中引发异常?

代码:

# coding=utf-8
import pytest




def whatever():
return 9/0


def test_whatever():
try:
whatever()
except ZeroDivisionError as exc:
pytest.fail(exc, pytrace=True)

输出:

================================ test session starts =================================
platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2
plugins: django, cov
collected 1 items


pytest_test.py F


====================================== FAILURES ======================================
___________________________________ test_whatever ____________________________________


def test_whatever():
try:
whatever()
except ZeroDivisionError as exc:
>           pytest.fail(exc, pytrace=True)
E           Failed: integer division or modulo by zero


pytest_test.py:12: Failed
============================== 1 failed in 1.16 seconds ==============================

如何使pytest打印回溯,这样我就会看到在whatever函数异常被引发的地方?

450661 次浏览

你的意思是这样的吗?

def test_raises():
with pytest.raises(Exception) as exc_info:
raise Exception('some info')
# these asserts are identical; you can use either one
assert exc_info.value.args[0] == 'some info'
assert str(exc_info.value) == 'some info'

pytest.raises(Exception)是你需要的。

代码

import pytest


def test_passes():
with pytest.raises(Exception) as e_info:
x = 1 / 0


def test_passes_without_info():
with pytest.raises(Exception):
x = 1 / 0


def test_fails():
with pytest.raises(Exception) as e_info:
x = 1 / 1


def test_fails_without_info():
with pytest.raises(Exception):
x = 1 / 1


# Don't do this. Assertions are caught as exceptions.
def test_passes_but_should_not():
try:
x = 1 / 1
assert False
except Exception:
assert True


# Even if the appropriate exception is caught, it is bad style,
# because the test result is less informative
# than it would be with pytest.raises(e)
# (it just says pass or fail.)


def test_passes_but_bad_style():
try:
x = 1 / 0
assert False
except ZeroDivisionError:
assert True


def test_fails_but_bad_style():
try:
x = 1 / 1
assert False
except ZeroDivisionError:
assert True

输出

============================================================================================= test session starts ==============================================================================================
platform linux2 -- Python 2.7.6 -- py-1.4.26 -- pytest-2.6.4
collected 7 items


test.py ..FF..F


=================================================================================================== FAILURES ===================================================================================================
__________________________________________________________________________________________________ test_fails __________________________________________________________________________________________________


def test_fails():
with pytest.raises(Exception) as e_info:
>           x = 1 / 1
E           Failed: DID NOT RAISE


test.py:13: Failed
___________________________________________________________________________________________ test_fails_without_info ____________________________________________________________________________________________


def test_fails_without_info():
with pytest.raises(Exception):
>           x = 1 / 1
E           Failed: DID NOT RAISE


test.py:17: Failed
___________________________________________________________________________________________ test_fails_but_bad_style ___________________________________________________________________________________________


def test_fails_but_bad_style():
try:
x = 1 / 1
>           assert False
E           assert False


test.py:43: AssertionError
====================================================================================== 3 failed, 4 passed in 0.02 seconds ======================================================================================

注意,e_info保存了异常对象,以便您可以从中提取细节。例如,如果您想检查异常调用堆栈或内部的另一个嵌套异常。

您是否尝试删除“pytrace=True”?

pytest.fail(exc, pytrace=True) # before
pytest.fail(exc) # after

你试过用“——fulltrace”运行吗?

更好的做法是使用继承unittest的类。TestCase和运行self.assertRaises。

例如:

import unittest




def whatever():
return 9/0




class TestWhatEver(unittest.TestCase):


def test_whatever():
with self.assertRaises(ZeroDivisionError):
whatever()

然后运行以下命令执行:

pytest -vs test_path

你可以试试

def test_exception():
with pytest.raises(Exception) as excinfo:
function_that_raises_exception()
assert str(excinfo.value) == 'some info'

在pytest中有两种方法来处理这类情况:

  • 使用pytest.raises函数

  • 使用pytest.mark.xfail装饰器

正如文档所说:

在测试自己的代码故意引发的异常的情况下,使用pytest.raises可能更好,而在检查函数中使用@pytest.mark.xfail可能更好地用于记录未修复的错误(其中测试描述了“应该”发生什么)或依赖关系中的错误。

使用pytest.raises:

def whatever():
return 9/0
def test_whatever():
with pytest.raises(ZeroDivisionError):
whatever()

使用pytest.mark.xfail:

@pytest.mark.xfail(raises=ZeroDivisionError)
def test_whatever():
whatever()

pytest.raises输出:

============================= test session starts ============================
platform linux2 -- Python 2.7.10, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 --
/usr/local/python_2.7_10/bin/python
cachedir: .cache
rootdir: /home/user, inifile:
collected 1 item


test_fun.py::test_whatever PASSED




======================== 1 passed in 0.01 seconds =============================

pytest.xfail标记的输出:

============================= test session starts ============================
platform linux2 -- Python 2.7.10, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 --
/usr/local/python_2.7_10/bin/python
cachedir: .cache
rootdir: /home/user, inifile:
collected 1 item


test_fun.py::test_whatever xfail


======================== 1 xfailed in 0.03 seconds=============================

这个解决方案是我们正在使用的:

def test_date_invalidformat():
"""
Test if input incorrect data will raises ValueError exception
"""
date = "06/21/2018 00:00:00"
with pytest.raises(ValueError):
app.func(date) #my function to be tested

请参考pytest, https://docs.pytest.org/en/latest/reference.html#pytest-raises

正确的方法是使用pytest.raises,但我在在这里的评论中发现了另一种有趣的方法,我想把它留给未来的读者:

try:
thing_that_rasises_typeerror()
assert False
except TypeError:
assert True

pytest在不断地发展,最近有了一个很好的变化,现在可以对同时进行测试了

  • 异常类型(严格测试)
  • 错误消息(使用正则表达式进行严格或松散检查)

文档中的两个例子:

with pytest.raises(ValueError, match='must be 0 or None'):
raise ValueError('value must be 0 or None')
with pytest.raises(ValueError, match=r'must be \d+$'):
raise ValueError('value must be 42')

我已经在许多项目中使用了这种方法,并且非常喜欢它。

< p >注意: 这样的评论 by ilya-rusin也建议上述方法

pytest中有两种方法来处理异常:

  1. 使用pytest.raises编写关于引发异常的断言
  2. 使用# EYZ0

1. 使用# EYZ0

从# EYZ0:

为了编写关于引发异常的断言,可以使用pytest.raises作为上下文管理器

例子:

只主张一个例外:

import pytest




def test_zero_division():
with pytest.raises(ZeroDivisionError):
1 / 0

with pytest.raises(ZeroDivisionError)表示无论什么 在下一段代码中应该引发ZeroDivisionError异常。如果没有引发异常,则测试失败。如果测试引发不同的异常,则测试失败

如果你需要访问实际的异常信息:

import pytest


def f():
f()


def test_recursion_depth():
with pytest.raises(RuntimeError) as excinfo:
f()
assert "maximum recursion" in str(excinfo.value)

excinfoExceptionInfo实例,它是实际引发的异常的包装器。感兴趣的主要属性是.type.value.traceback

2. 使用# EYZ0

也可以将raises参数指定为pytest.mark.xfail

import pytest


@pytest.mark.xfail(raises=IndexError)
def test_f():
l = [1, 2, 3]
l[10]

@pytest.mark.xfail(raises=IndexError)表示无论什么 在下一段代码中应该引发IndexError异常。如果抛出IndexError,则测试被标记为xfailed (x)。如果没有引发异常,则将测试标记为xpassed (X)。如果测试引发不同的异常,则测试失败

注:

  • 使用pytest.raises可能更好地用于测试您自己的代码故意引发的异常的情况,而使用@pytest.mark.xfail和检查函数可能更好地用于记录未修复的错误或依赖关系中的错误。

  • 您可以将match关键字参数传递给上下文管理器(pytest.raises),以测试正则表达式是否与异常的字符串表示匹配。(# EYZ2)

如果你想测试特定错误类型,使用try, catch和raise的组合:

#-- test for TypeError
try:
myList.append_number("a")
assert False
except TypeError: pass
except: assert False

如果您希望在测试用例中引发异常,那么这里提交的顶部答案是有用的。如果您的测试 引发了一个异常,并且您希望在两种情况下都能够优雅地处理它,那么这种方法就不太有用了。

如果你有一个测试案例,(不会)提出异常,我认为这可能是一个更好的选择。

@python.mark.parametrize("request_query, response_code", query_response_dataset)
def test_big_query_submission(request_query, response_code):
try:
stats = bigquery.Client().query(request_query)
except Exception as e:
assert False, f"Exception raised: {e}"
assert stats.errors is None

通过这种方式,您可以优雅地失败一个测试,而不是由于任何原因通过引发的异常崩溃一个测试。

我刚写了一个钩子,每个测试都有

钩子:

@ pytest. hookimpl (tryfirst = True,hookwrapper = True) Def pytest _ runtest _ makereport (Item: Item,call: CallInfo) :

    outcome = yield  # The result after the test is completed
result = outcome.get_result()


if result.when == "call":
if result.failed == True:


else:

我有一个函数,它用测试结果更新文档,并使用: result t.longgrepr.reprcrash.message 添加跟踪

我不知道这是否是最好的方法,但它确实回答了如何只使用 pytest 打印回溯的问题

@ 显然我还有别的密码

只是添加另一个“愚蠢”的建议,因为我没有看到它在现有的答案。实际上,您可以将一个错误变量初始化为 None,在 try/竹节中执行 do-the-thing,然后检查错误变量的 class/value

e = None


try:
blah()
except Exception as exc:
e = exc


assert e.__class__ == ValueError # or whatever you expect
assert str(e) == "expected message"