如何编写只有在函数没有抛出预期异常时才会失败的单元测试呢?


当前回答

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

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只检查是否引发了异常。

其他回答

由于我还没有看到任何关于如何检查我们是否在使用上下文管理器的接受列表中获得特定异常的详细解释,或其他异常细节,我将添加我的(在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])

使用TestCase。从unittest模块中assertRaises(或testcase . failunlessraised),例如:

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:
       self.fail('unexpected exception raised')
    else:
       self.fail('ExpectedException not raised')

在Python < 2.7中,此构造用于检查预期异常中的特定值。unittest函数assertRaises只检查是否引发了异常。

如何测试Python函数是否抛出异常? 如何编写只有在函数不抛出时才会失败的测试呢 意料之中的例外?

简短的回答:

利用自我。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')

测试一使用assertRaises作为上下文管理器,以确保在记录错误的同时正确地捕获和清除错误。

我们也可以在没有上下文管理器的情况下编写它,参见测试二。第一个参数是您期望引发的错误类型,第二个参数是您正在测试的函数,其余的args和关键字args将被传递给该函数。

我认为仅仅使用上下文管理器要简单得多,可读性和可维护性更强。

运行测试

要运行测试:

unittest.main(exit=False)

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

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

你的终端应该输出如下:

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

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