我们如何测试Python函数引发异常?

时间:2020-03-06 14:41:10  来源:igfitidea点击:

如何编写仅在函数未引发预期异常的情况下才会失败的单元测试?

解决方案

看一看unittest模块的assertRaises方法。

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

import mymod

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

代码应遵循以下模式(这是一个unittest模块样式测试):

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上,此构造对于检查预期异常中的特定值很有用。 unittest函数" assertRaises"仅检查是否引发了异常。

我上一个答案中的代码可以简化为:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction)

如果函数接受参数,则将其传递给assertRaises,如下所示:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction, arg1, arg2)

我几乎在所有地方都使用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示例

我刚刚发现Mock库提供了assertRaisesWithMessage()方法(在其unittest.TestCase子类中),该方法不仅会检查是否引发了预期的异常,还检查了与预期消息一起引发的异常:

from testcase import TestCase

import mymod

class MyTestCase(TestCase):
    def test1(self):
        self.assertRaisesWithMessage(SomeCoolException,
                                     'expected message',
                                     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))

我们可以使用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)