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


当前回答

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

其他回答

我之前回答中的代码可以简化为:

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

如果一个函数有参数,就像这样把它们传递给assertRaises:

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

使用unittest进行单元测试将是首选,但如果您想快速修复,我们可以捕获异常,将其赋值给一个变量,并查看该变量是否是该异常类的实例。

假设我们的坏函数抛出一个ValueError。

    try:
      bad_function()
    except ValueError as e:
      assert isinstance(e, ValueError)

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

断言


在Python 3.5中,必须对上下文进行包装。否则你会得到一个TypeError

self.assertTrue('This is broken' in str(context.exception))

你的代码应该遵循这个模式(这是一个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只检查是否引发了异常。

对于await/async aiounittest有一个稍微不同的模式:

https://aiounittest.readthedocs.io/en/latest/asynctestcase.html#aiounittest.AsyncTestCase

async def test_await_async_fail(self):
    with self.assertRaises(Exception) as e:
        await async_one()