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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23337471/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 02:47:01  来源:igfitidea点击:

How to properly assert that an exception gets raised in pytest?

pythonunit-testingtestingpytest

提问by Gill Bates

Code:

代码:

# coding=utf-8
import pytest


def whatever():
    return 9/0

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

Output:

输出:

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

How to make pytest print traceback, so I would see where in the whateverfunction an exception was raised?

如何进行 pytest 打印回溯,以便查看whatever函数中的何处引发异常?

采纳答案by Murilo Giacometti

pytest.raises(Exception)is what you need.

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

Code

代码

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

Output

输出

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

Note that e_infosaves the exception object so you can extract details from it. For example, if you want to check the exception call stack or another nested exception inside.

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

回答by simpleranchero

Do you mean something like this:

你的意思是这样的:

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'

回答by Loic Pantou

Have you tried to remove "pytrace=True" ?

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

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

Have you tried to run with '--fulltrace' ?

您是否尝试使用 '--fulltrace' 运行?

回答by kerbelp

Better practice will be using a class that inherit unittest.TestCase and running self.assertRaises.

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

For example:

例如:

import unittest


def whatever():
    return 9/0


class TestWhatEver(unittest.TestCase):

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

Then you would execute it by running:

然后你可以通过运行来执行它:

pytest -vs test_path

回答by d_j

you can try

你可以试试

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

回答by veri_pudicha_coder

There are two ways to handle these kind of cases in pytest:

在 pytest 中有两种处理此类情况的方法:

  • Using pytest.raisesfunction

  • Using pytest.mark.xfaildecorator

  • 使用pytest.raises功能

  • 使用pytest.mark.xfail装饰器

Usage of pytest.raises:

用法pytest.raises

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

Usage of pytest.mark.xfail:

用法pytest.mark.xfail

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

Output of pytest.raises:

的输出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 =============================

Output of pytest.xfailmarker:

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

As the documentationsays:

正如文档所说:

Using pytest.raisesis likely to be better for cases where you are testing exceptions your own code is deliberately raising, whereas using @pytest.mark.xfailwith a check function is probably better for something like documenting unfixed bugs (where the test describes what “should” happen) or bugs in dependencies.

使用pytest.raises很可能是更好的为你在哪里测试异常你自己的代码被刻意提高的情况下,而使用@pytest.mark.xfail具有校验功能可能是更好的东西,像记录未修正的错误(如测试描述什么是“应该”发生)或错误的依赖.

回答by SMDC

This solution is what we are using:

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

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

Please refer to pytest, https://docs.pytest.org/en/latest/reference.html#pytest-raises

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

回答by Alexey Shrub

Right way is using pytest.raisesbut I found interesting alternative way in comments hereand want to save it for future readers of this question:

正确的方法是使用,pytest.raises但我在此处的评论中发现了有趣的替代方法,并希望将其保存以供此问题的未来读者使用:

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

回答by Dr. Jan-Philip Gehrcke

pytest constantly evolves and with one of the nice changes in the recent past it is now possible to simultaneouslytest for

pytest 不断发展,随着最近的一个很好的变化,现在可以同时测试

  • the exception type (strict test)
  • the error message (strict or loose check using a regular expression)
  • 异常类型(严格测试)
  • 错误消息(使用正则表达式进行严格或宽松检查)

Two examples from the documentation:

文档中的两个示例:

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

I have been using that approach in a number of projects and like it very much.

我一直在许多项目中使用这种方法并且非常喜欢它。