Dar*_*zer 695 python unit-testing exception-handling exception
如果一个函数没有抛出预期的异常,那么如何编写一个单元测试失败?
Moe*_*Moe 605
使用TestCase.assertRaises
(或TestCase.failUnlessRaises
)来自unittest模块,例如:
import mymod
class MyTestCase(unittest.TestCase):
def test1(self):
self.assertRaises(SomeCoolException, mymod.myfunc)
Run Code Online (Sandbox Code Playgroud)
Art*_*Art 434
从Python 2.7开始,您可以使用上下文管理器来获取抛出的实际Exception对象:
import unittest
def broken_function():
raise Exception('This is broken')
class MyTestCase(unittest.TestCase):
def test(self):
with self.assertRaises(Exception) as context:
broken_function()
self.assertTrue('This is broken' in context.exception)
if __name__ == '__main__':
unittest.main()
Run Code Online (Sandbox Code Playgroud)
http://docs.python.org/dev/library/unittest.html#unittest.TestCase.assertRaises
在Python的3.5,你必须包装context.exception
在str
,否则,你会得到一个TypeError
self.assertTrue('This is broken' in str(context.exception))
Run Code Online (Sandbox Code Playgroud)
Dar*_*zer 287
我之前回答中的代码可以简化为:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction)
Run Code Online (Sandbox Code Playgroud)
如果函数接受参数,只需将它们传递给assertRaises,如下所示:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction, arg1, arg2)
Run Code Online (Sandbox Code Playgroud)
Aar*_*all 120
你如何测试Python函数抛出异常?
如果函数没有抛出预期的异常,那么如何编写一个失败的测试呢?
将该self.assertRaises
方法用作上下文管理器:
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
Run Code Online (Sandbox Code Playgroud)
最佳实践方法很容易在Python shell中演示.
该unittest
库
在Python 2.7或3中:
import unittest
Run Code Online (Sandbox Code Playgroud)
在Python 2.6中,您可以安装2.7的unittest
库的backport ,称为unittest2,并将其别名为unittest
:
import unittest2 as unittest
Run Code Online (Sandbox Code Playgroud)
现在,将以下Python类型安全性测试粘贴到Python shell中:
class MyTestCase(unittest.TestCase):
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
def test_2_cannot_add_int_and_str(self):
import operator
self.assertRaises(TypeError, operator.add, 1, '1')
Run Code Online (Sandbox Code Playgroud)
测试1 assertRaises
用作上下文管理器,确保在记录时正确捕获和清除错误.
我们也可以在没有上下文管理器的情况下编写它,参见测试二.第一个参数是您希望引发的错误类型,第二个参数,您正在测试的函数,剩余的args和关键字args将传递给该函数.
我认为只使用上下文管理器更简单,可读和可维护.
要运行测试:
unittest.main(exit=False)
Run Code Online (Sandbox Code Playgroud)
在Python 2.6中,您可能需要以下内容:
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Run Code Online (Sandbox Code Playgroud)
您的终端应输出以下内容:
..
----------------------------------------------------------------------
Ran 2 tests in 0.007s
OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>
Run Code Online (Sandbox Code Playgroud)
我们看到正如我们所期望的那样,尝试在a中添加a 1
和a '1'
结果TypeError
.
要获得更详细的输出,请尝试以下方法:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Run Code Online (Sandbox Code Playgroud)
Dar*_*zer 52
您的代码应遵循此模式(这是一个单元测试模块样式测试):
def test_afunction_throws_exception(self):
try:
afunction()
except ExpectedException:
pass
except Exception:
self.fail('unexpected exception raised')
else:
self.fail('ExpectedException not raised')
Run Code Online (Sandbox Code Playgroud)
在Python <2.7上,此构造对于检查预期异常中的特定值很有用.unittest函数assertRaises
仅检查是否引发了异常.
kri*_*iss 29
由于我还没有看到关于如何使用上下文管理器检查我们是否在接受的异常列表中遇到特定异常或其他异常详细信息的任何详细说明,我将添加我的(在 python 3.8 上检查)。
例如,如果我只想检查该函数是否正在提升TypeError
,我会写:
with self.assertRaises(TypeError):
function_raising_some_exception(parameters)
Run Code Online (Sandbox Code Playgroud)
如果我想检查该函数是否正在引发TypeError
或IndexError
,我会写:
with self.assertRaises((TypeError,IndexError)):
function_raising_some_exception(parameters)
Run Code Online (Sandbox Code Playgroud)
如果我想要有关引发的异常的更多详细信息,我可以在这样的上下文中捕获它:
# Here I catch any exception
with self.assertRaises(Exception) as e:
function_raising_some_exception(parameters)
# Here I check actual exception type (but I could
# check anything else about that specific exception,
# like it's actual message or values stored in the exception)
self.assertTrue(type(e.exception) in [TypeError,MatrixIsSingular])
Run Code Online (Sandbox Code Playgroud)
ife*_*aju 20
如果您使用的是 Python 3,为了断言异常及其消息,您可以assertRaises
在上下文管理器中使用并将消息作为msg
关键字参数传递,如下所示:
import unittest
def your_function():
raise RuntimeError('your exception message')
class YourTestCase(unittest.TestCase):
def test(self):
with self.assertRaises(RuntimeError, msg='your exception message'):
your_function()
if __name__ == '__main__':
unittest.main()
Run Code Online (Sandbox Code Playgroud)
mac*_*acm 18
来自:http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/
首先,这是文件dum_function.py中相应的(仍为dum:p)函数:
def square_value(a):
"""
Returns the square value of a.
"""
try:
out = a*a
except TypeError:
raise TypeError("Input should be a string:")
return out
Run Code Online (Sandbox Code Playgroud)
这是要执行的测试(仅插入此测试):
import dum_function as df # import function module
import unittest
class Test(unittest.TestCase):
"""
The class inherits from unittest
"""
def setUp(self):
"""
This method is called before each test
"""
self.false_int = "A"
def tearDown(self):
"""
This method is called after each test
"""
pass
#---
## TESTS
def test_square_value(self):
# assertRaises(excClass, callableObj) prototype
self.assertRaises(TypeError, df.square_value(self.false_int))
if __name__ == "__main__":
unittest.main()
Run Code Online (Sandbox Code Playgroud)
我们现在准备测试我们的功能了!以下是尝试运行测试时发生的情况:
======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_dum_function.py", line 22, in test_square_value
self.assertRaises(TypeError, df.square_value(self.false_int))
File "/home/jlengrand/Desktop/function.py", line 8, in square_value
raise TypeError("Input should be a string:")
TypeError: Input should be a string:
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
Run Code Online (Sandbox Code Playgroud)
TypeError会引发actullay,并生成测试失败.问题是这正是我们想要的行为:s.
要避免此错误,只需在测试调用中使用lambda运行该函数:
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
Run Code Online (Sandbox Code Playgroud)
最终输出:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Run Code Online (Sandbox Code Playgroud)
完美!
......对我来说也是完美的!!
Thansk先生很多Julien Lengrand-Lambert先生
Pig*_*ras 12
您可以构建自己的,contextmanager
以检查是否引发了异常.
import contextlib
@contextlib.contextmanager
def raises(exception):
try:
yield
except exception as e:
assert True
else:
assert False
Run Code Online (Sandbox Code Playgroud)
然后你就可以这样使用raises
:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
Run Code Online (Sandbox Code Playgroud)
如果你正在使用pytest
,这个东西已经实现了.你可以这样做pytest.raises(Exception)
:
例:
def test_div_zero():
with pytest.raises(ZeroDivisionError):
1/0
Run Code Online (Sandbox Code Playgroud)
结果如下:
pigueiras@pigueiras$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items
tests/test_div_zero.py:6: test_div_zero PASSED
Run Code Online (Sandbox Code Playgroud)
pi.*_*pi. 11
我几乎到处都使用doctest [1]因为我喜欢同时记录和测试我的函数的事实.
看看这段代码:
def throw_up(something, gowrong=False):
"""
>>> throw_up('Fish n Chips')
Traceback (most recent call last):
...
Exception: Fish n Chips
>>> throw_up('Fish n Chips', gowrong=True)
'I feel fine!'
"""
if gowrong:
return "I feel fine!"
raise Exception(something)
if __name__ == '__main__':
import doctest
doctest.testmod()
Run Code Online (Sandbox Code Playgroud)
如果将此示例放在模块中并从命令行运行它,则会评估和检查两个测试用例.
[1] Python文档:23.2 doctest - 测试交互式Python示例
我刚刚发现Mock库提供了一个assertRaisesWithMessage()方法(在其unittest.TestCase子类中),它不仅会检查是否引发了预期的异常,而且还会检查它是否带有预期的消息:
from testcase import TestCase
import mymod
class MyTestCase(TestCase):
def test1(self):
self.assertRaisesWithMessage(SomeCoolException,
'expected message',
mymod.myfunc)
Run Code Online (Sandbox Code Playgroud)
这里有很多答案。代码显示了我们如何创建异常,如何在我们的方法中使用该异常,最后,如何在单元测试中验证引发的正确异常。
import unittest
class DeviceException(Exception):
def __init__(self, msg, code):
self.msg = msg
self.code = code
def __str__(self):
return repr("Error {}: {}".format(self.code, self.msg))
class MyDevice(object):
def __init__(self):
self.name = 'DefaultName'
def setParameter(self, param, value):
if isinstance(value, str):
setattr(self, param , value)
else:
raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)
def getParameter(self, param):
return getattr(self, param)
class TestMyDevice(unittest.TestCase):
def setUp(self):
self.dev1 = MyDevice()
def tearDown(self):
del self.dev1
def test_name(self):
""" Test for valid input for name parameter """
self.dev1.setParameter('name', 'MyDevice')
name = self.dev1.getParameter('name')
self.assertEqual(name, 'MyDevice')
def test_invalid_name(self):
""" Test to check if error is raised if invalid type of input is provided """
self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)
def test_exception_message(self):
""" Test to check if correct exception message and code is raised when incorrect value is passed """
with self.assertRaises(DeviceException) as cm:
self.dev1.setParameter('name', 1234)
self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')
if __name__ == '__main__':
unittest.main()
Run Code Online (Sandbox Code Playgroud)
您可以使用单元测试模块中的assertRaises:
\nimport unittest\n\nclass TestClass():\n def raises_exception(self):\n raise Exception("test")\n\nclass MyTestCase(unittest.TestCase):\n def test_if_method_raises_correct_exception(self):\n test_class = TestClass()\n # Note that you don\xe2\x80\x99t use () when passing the method to assertRaises\n self.assertRaises(Exception, test_class.raises_exception)\n
Run Code Online (Sandbox Code Playgroud)\n
有 4 个选项(您将在最后找到完整的示例):
def test_raises(self):
with self.assertRaises(RuntimeError):
raise RuntimeError()
Run Code Online (Sandbox Code Playgroud)
如果您想检查异常消息(请参阅下面的“assertRaisesRegex with context manager”选项以仅检查其中的一部分):
def test_raises(self):
with self.assertRaises(RuntimeError) as error:
raise RuntimeError("your exception message")
self.assertEqual(str(error.exception), "your exception message")
Run Code Online (Sandbox Code Playgroud)
注意:这里您使用的是可调用函数(不带圆括号),而不是函数调用。
def test_raises(self):
self.assertRaises(RuntimeError, your_function)
Run Code Online (Sandbox Code Playgroud)
第二个参数是正则表达式,并且是必需的。当您只想检查部分异常消息时非常方便。
def test_raises_regex(self):
with self.assertRaisesRegex(RuntimeError, r'.* exception message'):
raise RuntimeError('your exception message')
Run Code Online (Sandbox Code Playgroud)
第二个参数是正则表达式,并且是必需的。当您只想检查部分异常消息时非常方便。
注意:这里您使用的是可调用函数(不带圆括号),而不是函数调用。
def test_raises_regex(self):
self.assertRaisesRegex(RuntimeError, r'.* exception message', your_function)
Run Code Online (Sandbox Code Playgroud)
import unittest
def your_function():
raise RuntimeError('your exception message')
class YourTestCase(unittest.TestCase):
def test_1_raises_context_manager(self):
with self.assertRaises(RuntimeError):
your_function()
def test_1b_raises_context_manager_and_error_message(self):
with self.assertRaises(RuntimeError) as error:
your_function()
self.assertEqual(str(error.exception), "your exception message")
def test_2_raises_oneliner(self):
self.assertRaises(RuntimeError, your_function)
def test_3_raises_regex_context_manager(self):
with self.assertRaisesRegex(RuntimeError, r'.* exception message'):
your_function()
def test_4_raises_regex_oneliner(self):
self.assertRaisesRegex(RuntimeError, r'.* exception message', your_function)
if __name__ == '__main__':
unittest.main()
Run Code Online (Sandbox Code Playgroud)
虽然遵循哪种风格取决于开发人员,但我更喜欢使用上下文管理器的两种方法。