如何编写只有在函数没有抛出预期异常时才会失败的单元测试呢?
当前回答
有4个选项(你会在最后找到完整的例子):
assertRaises与上下文管理器
def test_raises(self):
with self.assertRaises(RuntimeError):
raise RuntimeError()
如果你想检查异常消息(参见下面的"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")
assertRaises一行程序
注意:这里使用的函数不是函数调用,而是可调用函数(不带圆括号)。
def test_raises(self):
self.assertRaises(RuntimeError, your_function)
assertRaisesRegex与上下文管理器
第二个参数是正则表达式,是必选项。当您只想检查部分异常消息时,非常方便。
def test_raises_regex(self):
with self.assertRaisesRegex(RuntimeError, r'.* exception message'):
raise RuntimeError('your exception message')
assertRaisesRegex一行程序
第二个参数是正则表达式,是必选项。当您只想检查部分异常消息时,非常方便。
注意:这里使用的函数不是函数调用,而是可调用函数(不带圆括号)。
def test_raises_regex(self):
self.assertRaisesRegex(RuntimeError, r'.* exception message', your_function)
完整的代码示例:
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()
虽然这取决于开发人员遵循哪种风格,但我更喜欢使用上下文管理器的两种方法。
其他回答
如果你正在使用pytest,你可以使用pytest.raise (Exception):
例子:
def test_div_zero():
with pytest.raises(ZeroDivisionError):
1/0
结果是:
$ 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
或者你可以构建自己的上下文管理器来检查异常是否被引发。
import contextlib
@contextlib.contextmanager
def raises(exception):
try:
yield
except exception as e:
assert True
else:
assert False
然后你可以像这样使用加薪:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
使用unittest进行单元测试将是首选,但如果您想快速修复,我们可以捕获异常,将其赋值给一个变量,并查看该变量是否是该异常类的实例。
假设我们的坏函数抛出一个ValueError。
try:
bad_function()
except ValueError as e:
assert isinstance(e, ValueError)
从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
下面是要执行的测试(只插入这个测试):
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()
现在我们准备好测试我们的函数了!下面是运行测试时发生的情况:
======================================================================
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)
TypeError实际上被引发,并生成一个测试失败。问题是,这正是我们想要的行为:s。
要避免此错误,只需在测试调用中使用lambda运行函数:
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
最终输出:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
完美!
... 对我来说也是完美的!!
非常感谢,Julien Lengrand-Lambert先生。
这个测试断言实际上返回一个假阳性。这是因为'assertRaises'中的lambda是引发类型错误的单元,而不是被测试的函数。
由于我还没有看到任何关于如何检查我们是否在使用上下文管理器的接受列表中获得特定异常的详细解释,或其他异常细节,我将添加我的(在Python 3.8上检查)。
如果我只是想检查函数是否引发实例TypeError,我会写:
with self.assertRaises(TypeError):
function_raising_some_exception(parameters)
如果我想检查函数是否引发TypeError或IndexError,我会写:
with self.assertRaises((TypeError,IndexError)):
function_raising_some_exception(parameters)
如果我想要更多关于异常引发的细节,我可以在这样的上下文中捕获它:
# 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])
我几乎在任何地方都使用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()
如果您把这个例子放在一个模块中,并从命令行运行它,两个测试用例都会被评估和检查。
[1] Python文档:23.2 doctest——测试交互式Python示例