首页 文章

你如何测试Python函数抛出异常?

提问于
浏览
542

如果一个函数没有抛出预期的异常,那么如何写一个单元测试失败?

11 回答

  • 98

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

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

    从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 中,你必须在 str 中包装 context.exception ,否则你将获得 TypeError

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

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

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

    如果功能接受参数,只需将它们传递给assertRaises,如下所示:

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

    您如何测试Python函数是否会引发异常?如果函数没有抛出预期的异常,如何编写一个失败的测试?

    简答:

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

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

    示范

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

    The unittest library

    在Python 2.7或3中:

    import unittest
    

    在Python 2.6中,您可以安装一个2.7的 unittest 库的反向端口,名为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')
    

    测试1使用 assertRaises 作为上下文管理器,确保在记录时正确捕获和清除错误 .

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

    我认为只使用上下文管理器更简单,可读和可维护 .

    运行测试

    要运行测试:

    unittest.main(exit=False)
    

    在Python 2.6中,你可能会need the following

    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))
    
  • 8

    您的代码应遵循此模式(这是一个单元测试模块样式测试):

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

  • 5

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

    这是要执行的测试(仅插入此测试):

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

    我们现在准备测试我们的功能了!以下是尝试运行测试时发生的情况:

    ======================================================================
    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会引发actullay,并生成测试失败 . 问题是这正是我们想要的行为:s .

    要避免此错误,只需在测试调用中使用lambda运行该函数:

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

    最终输出:

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

    完美!

    ......对我来说也是完美的!!

    Thansk先生很多Julien Lengrand-Lambert先生

  • 1

    我几乎到处都使用 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 documentation: 23.2 doctest -- Test interactive Python examples

  • 12

    看一下 unittest 模块的assertRaises方法 .

  • 336

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

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

    您可以构建自己的 contextmanager 以检查是否引发了异常 .

    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
    
  • 32

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

相关问题