你如何testing一个Python函数抛出一个exception?

如何编写一个unittest,只有当一个函数没有抛出预期的exception时才会失败?

使用unittest模块中的TestCase.assertRaises (或TestCase.failUnlessRaises ),例如:

 import mymod class MyTestCase(unittest.TestCase): def test1(self): self.assertRaises(SomeCoolException, mymod.myfunc) 

从Python 2.7开始,您可以使用上下文pipe理器来获取抛出的实际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中 ,你必须在str包装context.exception ,否则你会得到一个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) 

你如何testing一个Python函数抛出一个exception?

如何编写一个只有在函数不抛出预期exception的情况下才会失败的testing?

简答:

使用self.assertRaises方法作为上下文pipe理器:

  def test_1_cannot_add_int_and_str(self): with self.assertRaises(TypeError): 1 + '1' 

示范

最佳实践方法在Python shell中很容易演示。

unit testing库

在Python 2.7或3中:

 import unittest 

在Python 2.6中,你可以安装一个名为unittest2的2.7unit testing库的backport,并且将其作为unit testing的别名:

 import unittest2 as unittest 

示例testing

现在,将Python的types安全性testing粘贴到您的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') 

testing人员使用assertRaises作为上下文pipe理器,确保在logging时正确捕获和清除错误。

我们也可以在没有上下文pipe理器的情况下编写它,参见testing二。 第一个参数是你期望提出的错误types,第二个参数,你正在testing的函数,其余的参数和关键字参数将被传递给该函数。

我认为它更简单,更易读,更易于使用上下文pipe理器。

运行testing

运行testing:

 unittest.main(exit=False) 

在Python 2.6中,您可能需要以下内容 :

 unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase)) 

你的terminal应该输出以下内容:

 .. ---------------------------------------------------------------------- Ran 2 tests in 0.007s OK <unittest2.runner.TextTestResult run=2 errors=0 failures=0> 

我们看到,正如我们所期望的那样,试图在TypeError添加1'1'结果。


更详细的输出,试试这个:

 unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase)) 

你的代码应该遵循这个模式(这是一个unit testing模块风格testing):

 def test_afunction_throws_exception(self): try: afunction() except ExpectedException: pass except Exception as e: self.fail('Unexpected exception raised:', e) else: self.fail('ExpectedException not raised') 

在Python <2.7这个结构对检查预期exception中的特定值很有用。 unittest函数assertRaises只检查是否引发exception。

我几乎在任何地方都使用doctest [1],因为我喜欢同时logging和testing我的function的事实。

看看这个代码:

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

如果你把这个例子放在一个模块中并从命令行运行,那么这两个testing用例都将被评估和检查。

[1] Python文档:23.2 doctest – testing交互式Python示例

来自: 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 

这里是要执行的testing(只有这个testing被插入):

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

我们现在准备testing我们的function! 这是试图运行testing时发生的事情:

 ====================================================================== 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被动作提出,并产生一个testing失败。 问题是,这正是我们想要的行为:s。

为了避免这个错误,只需在testing调用中使用lambda来运行函数:

 self.assertRaises(TypeError, lambda: df.square_value(self.false_int)) 

最终输出:

 ---------------------------------------------------------------------- Ran 1 test in 0.000s OK 

完美!

…对我来说也是完美的!

Thansk很多Julien Lengrand-Lambert先生

看看unittest模块的assertRaises方法。

我刚刚发现, Mock库提供了一个assertRaisesWithMessage()方法(在它的unittest.TestCase子类中),它不仅检查期望的exception是否被引发,而且还会引发预期的消息:

 from testcase import TestCase import mymod class MyTestCase(TestCase): def test1(self): self.assertRaisesWithMessage(SomeCoolException, 'expected message', mymod.myfunc) 

您可以构build自己的contextmanager来检查是否引发exception。

 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 

你可以使用unittest模块中的assertRaises

 import unittest class TestClass(): def raises_exception(self): raise Exception("test") class MyTestCase(unittest.TestCase): def test_if_method_raises_correct_exception(self): test_class = TestClass() # note that you dont use () when passing the method to assertRaises self.assertRaises(Exception, test_class.raises_exception)