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
How to properly assert that an exception gets raised in pytest?
提问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 whatever
function 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_info
saves 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.raises
functionUsing
pytest.mark.xfail
decorator
使用
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.xfail
marker:
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.raises
is likely to be better for cases where you are testing exceptions your own code is deliberately raising, whereas using@pytest.mark.xfail
with 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
回答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.
我一直在许多项目中使用这种方法并且非常喜欢它。