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


当前回答

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

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示例

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

import mymod

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

有4个选项(你会在最后找到完整的例子):

assertRaises与上下文管理器

def test_raises(self):
    with self.assertRaises(RuntimeError):
        raise RuntimeError()

如果你想检查异常消息(参见下面的"assertRaisesRegex with context manager"选项来检查它的一部分):

def test_raises(self):
    with self.assertRaises(RuntimeError) as error:
        raise RuntimeError("your exception message")
    self.assertEqual(str(error.exception), "your exception message")

assertRaises一行程序

注意:这里使用的函数不是函数调用,而是可调用函数(不带圆括号)。

def test_raises(self):
    self.assertRaises(RuntimeError, your_function)

assertRaisesRegex与上下文管理器

第二个参数是正则表达式,是必选项。当您只想检查部分异常消息时,非常方便。

def test_raises_regex(self):
    with self.assertRaisesRegex(RuntimeError, r'.* exception message'):
        raise RuntimeError('your exception message')

assertRaisesRegex一行程序

第二个参数是正则表达式,是必选项。当您只想检查部分异常消息时,非常方便。

注意:这里使用的函数不是函数调用,而是可调用函数(不带圆括号)。

def test_raises_regex(self):
    self.assertRaisesRegex(RuntimeError, r'.* exception message', your_function)

完整的代码示例:

import unittest

def your_function():
    raise RuntimeError('your exception message')

class YourTestCase(unittest.TestCase):

    def test_1_raises_context_manager(self):
        with self.assertRaises(RuntimeError):
            your_function()

    def test_1b_raises_context_manager_and_error_message(self):
        with self.assertRaises(RuntimeError) as error:
            your_function()
        self.assertEqual(str(error.exception), "your exception message")

    def test_2_raises_oneliner(self):
        self.assertRaises(RuntimeError, your_function)

    def test_3_raises_regex_context_manager(self):
        with self.assertRaisesRegex(RuntimeError, r'.* exception message'):
            your_function()

    def test_4_raises_regex_oneliner(self):
        self.assertRaisesRegex(RuntimeError, r'.* exception message', your_function)

if __name__ == '__main__':
    unittest.main()

虽然这取决于开发人员遵循哪种风格,但我更喜欢使用上下文管理器的两种方法。

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

虽然所有的答案都很好,但我正在寻找一种方法来测试一个函数是否引发了异常,而不依赖于单元测试框架,也不必编写测试类。

最后我写下了以下内容:

def assert_error(e, x):
    try:
        e(x)
    except:
        return
    raise AssertionError()

def failing_function(x):
    raise ValueError()

def dummy_function(x):
    return x

if __name__=="__main__":
    assert_error(failing_function, 0)
    assert_error(dummy_function, 0)

它在右边行上失败了:

Traceback (most recent call last):
  File "assert_error.py", line 16, in <module>
    assert_error(dummy_function, 0)
  File "assert_error.py", line 6, in assert_error
    raise AssertionError()
AssertionError