代码:

# 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函数中引发了异常吗?

评论

我得到了整个追溯,Ubuntu 14.04,Python 2.7.6

@thefourtheye请根据输出使要领。我尝试使用Python 2.7.4和Ubuntu 14.04-与我在主帖子中描述的结果相同。

@GillBates可以标记正确的答案吗?

#1 楼

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保存异常对象,因此您可以从中提取详细信息。例如,如果要检查异常调用堆栈或内部的另一个嵌套异常。

评论


如果您可以包括一个实际查询e_info的示例,那将是很好的。对于更熟悉某些其他语言的开发人员来说,e_info的范围扩展到with块之外并不明显。

– cjs
18年5月30日在4:08

#2 楼

您的意思是这样的吗:

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


评论


excinfo.value.message对我不起作用,不得不使用str(excinfo.value),添加了新的答案

– d_j
17年5月24日在16:20



@d_j,断言excinfo.value.args [0] =='一些信息'是访问消息的直接方法

–maxschlepzig
17年7月2日在20:45

断言excinfo.match(r“ ^ some info $”)也可以

–罗宾·内梅斯(Robin Nemeth)
18年5月15日在9:03

从3.1版开始,您可以使用关键字参数match来断言该异常与文本或正则表达式匹配:使用raises(ValueError,match ='必须为0或None'):raise ValueError(“ value必须为0或None”)或与引发(ValueError,匹配= R'必须是\ d + $'):提高ValueError(“值必须为42”)

–伊利亚·鲁辛(Ilya Rusin)
18-10-5在7:01

#3 楼

在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=============================


评论


xfail不是解决问题的方法,它只是允许测试失败。在这里,我们要检查是否引发了某种异常。

– Ctrl-C
20年5月5日上午10:16

我必须重申@ Ctrl-C的评论:pytest.mark.xfail不会断言引发了异常,它只是允许引发异常。这不是问题标题所要的。

–迪恩·古维兹(Dean Gurvitz)
20/07/27在12:18

照原样,该答案具有误导性。说明xfail的文档段落应移至顶部。

–努拉诺
20年8月19日在21:21

@Nulano感谢您的建议。我已经更新了答案!

–veri_pudicha_coder
20年11月4日,10:39

#4 楼

您可以尝试

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


评论


为了在pytest 5.0.0中以字符串形式获取异常消息/值,需要使用str(excinfo.value)。它也可以在pytest 4.x中使用。在pytest 4.x中,str(excinfo)也可以,但是在pytest 5.0.0中不起作用。

–麦凯恩♦
19年6月29日在18:08



这就是我想要的。谢谢

–爱因斯坦再见
19-09-30在8:22

#5 楼

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')
 


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

#6 楼

正确的方法是使用pytest.raises,但我在这里的评论中找到了有趣的替代方法,并希望将其保存以供将来该问题的读者使用:

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


评论


这是坏风格。如果“事物”没有引发错误,也没有引发意外错误,则您将得到断言False,而没有测试报告或分析的任何上下文。 pytest追溯在断言处停止,目前尚不清楚要测试什么。即使是原始评论也提到了更好的方法,但是“仅仅是为了证明编写测试有多快”。为什么没有这种背景就可以“节省”呢?

–吉姆
20年8月24日在23:44

#7 楼

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

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-加薪

#8 楼

更好的做法是使用继承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


评论


比什么更好的做法?我根本不会使用unittest语法而不是pytest语法来称呼“更好的实践”。

–让·弗朗索瓦·科贝特(Jean-FrançoisCorbett)
17年11月14日在16:22

它可能不会“更好”,但是它是一个有用的选择。因为答案的标准是实用性,所以我很反对。

– Reb.Cabin
18年4月29日在17:39

看起来pytest比nasex更受欢迎,但这就是我使用pytest的方式。

– Gang
18年8月23日在22:31

#9 楼

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

使用pytest.raises编写有关引发的异常的断言
使用@pytest.mark.xfail



1。使用pytest.raises

从文档:

为了编写有关引发的异常的断言,可以将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。使用@pytest.mark.xfail

还可以为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)进行测试正则表达式匹配异常的字符串表示形式。 (查看更多)



#10 楼

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

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


您是否尝试过使用'--fulltrace'吗?

#11 楼

如果要测试特定的错误类型,请结合使用try,catch和raise:
#-- test for TypeError
try:
  myList.append_number("a")
  assert False
except TypeError: pass
except: assert False