如果一个函数没有抛出预期的异常,那么如何编写一个单元测试失败?
使用TestCase.assertRaises
(或TestCase.failUnlessRaises
)来自unittest模块,例如:
import mymod class MyTestCase(unittest.TestCase): def test1(self): self.assertRaises(SomeCoolException, mymod.myfunc)
从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()
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))
我之前回答中的代码可以简化为:
def test_afunction_throws_exception(self): self.assertRaises(ExpectedException, afunction)
如果函数接受参数,只需将它们传递给assertRaises,如下所示:
def test_afunction_throws_exception(self): self.assertRaises(ExpectedException, afunction, arg1, arg2)
简答:你如何测试Python函数抛出异常?
如果函数没有抛出预期的异常,那么如何编写一个失败的测试呢?
将该self.assertRaises
方法用作上下文管理器:
def test_1_cannot_add_int_and_str(self): with self.assertRaises(TypeError): 1 + '1'示范
最佳实践方法很容易在Python shell中演示.
该unittest
库
在Python 2.7或3中:
import unittest
在Python 2.6中,您可以安装2.7的unittest
库的backport ,称为unittest2,并将其别名为unittest
:
import unittest2 as unittest示例测试
现在,将以下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')
测试1 assertRaises
用作上下文管理器,确保在记录时正确捕获和清除错误.
我们也可以在没有上下文管理器的情况下编写它,参见测试二.第一个参数是您希望引发的错误类型,第二个参数,您正在测试的函数,剩余的args和关键字args将传递给该函数.
我认为只使用上下文管理器更简单,可读和可维护.
运行测试要运行测试:
unittest.main(exit=False)
在Python 2.6中,您可能需要以下内容:
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
您的终端应输出以下内容:
.. ---------------------------------------------------------------------- Ran 2 tests in 0.007s OK
我们看到正如我们所期望的那样,尝试在a中添加a 1
和a '1'
结果TypeError
.
要获得更详细的输出,请尝试以下方法:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
您的代码应遵循此模式(这是一个单元测试模块样式测试):
def test_afunction_throws_exception(self): try: afunction() except ExpectedException: pass except Exception: self.fail('unexpected exception raised') else: self.fail('ExpectedException not raised')
在Python <2.7上,此构造对于检查预期异常中的特定值很有用.unittest函数assertRaises
仅检查是否引发了异常.
来自: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会引发actullay,并生成测试失败.问题是这正是我们想要的行为:s.
要避免此错误,只需在测试调用中使用lambda运行该函数:
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
最终输出:
---------------------------------------------------------------------- Ran 1 test in 0.000s OK
完美!
......对我来说也是完美的!!
Thansk先生很多Julien Lengrand-Lambert先生
您可以构建自己的,contextmanager
以检查是否引发了异常.
import contextlib @contextlib.contextmanager def raises(exception): try: yield except exception as e: assert True else: assert False
然后你就可以这样使用raises
:
with raises(Exception): print "Hola" # Calls assert False with raises(Exception): raise Exception # Calls assert True
如果你正在使用pytest
,这个东西已经实现了.你可以这样做pytest.raises(Exception)
:
例:
def test_div_zero(): with pytest.raises(ZeroDivisionError): 1/0
结果如下:
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
我几乎到处都使用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示例
看看模块的assertRaises方法unittest
.
我刚刚发现Mock库提供了一个assertRaisesWithMessage()方法(在其unittest.TestCase子类中),它不仅会检查是否引发了预期的异常,而且还会检查它是否带有预期的消息:
from testcase import TestCase import mymod class MyTestCase(TestCase): def test1(self): self.assertRaisesWithMessage(SomeCoolException, 'expected message', mymod.myfunc)