当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《你如何测试Python函数抛出异常?》经验,为你挑选了10个好方法。

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



1> Moe..:

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

import mymod

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


请注意,要将参数传递给`myfunc`,您需要将它们作为参数添加到assertRaises调用中.请参阅Daryl Spitzer的答案.
有没有办法与此相反?就像它失败只有函数确实抛出异常?
测试类构造器的方法相同:self.assertRaises(SomeCoolException,Constructor,arg1)

2> Art..:

从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.exceptionstr,否则,你会得到一个TypeError

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


我使用的是Python 2.7.10,上面的功能不起作用; `context.exception`没有给出消息; 这是一种类型.
同样在Python 2.7(至少在我的2.7.6中)使用`import unittest2`,你需要使用`str()`,即`self.assertTrue('str(context.exception)中的'这是'')``.
两件事:1.您可以使用assertIn而不是assertTrue.例如self.assertIn('This is broken',context.exception)2.在我的例子中,使用2.7.10,context.exception似乎是一个字符数组.使用str不起作用.我最终这样做了:''.join(context.exception)所以,放在一起:self.assertIn('this is broken',''.join(context.exception))

3> Daryl Spitze..:

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

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

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

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


第二部分讨论了在传递参数时该做什么真的很有帮助.

4> Aaron Hall..:

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

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

简答:

将该self.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')

测试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

我们看到正如我们所期望的那样,尝试在a中添加a 1和a '1'结果TypeError.


要获得更详细的输出,请尝试以下方法:

unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))



5> Daryl Spitze..:

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

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


和方法self.fail只接受一个参数
如果函数抛出异常,这似乎过于复杂.由于除了该异常之外的任何异常都会导致测试错误并且不会抛出异常将导致测试失败,看起来唯一的区别是如果使用`assertRaises`获得不同的异常,则会得到ERROR而不是FAIL.

6> macm..:

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


只是一个注释,你不需要lambda.行`self.assertRaises(TypeError,df.square_value(self.false_int))`调用该方法并返回结果.你想要的是传递方法和任何参数,让unittest调用它:`self.assertRaises(TypeError,df.square_value,self.false_int)`

7> Pigueiras..:

您可以构建自己的,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



8> pi...:

我几乎到处都使用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示例


我喜欢doctest,但我觉得它补充而不是取代unittest.
doctest不太可能在自动重构中发挥出色吗?我想为python*设计的重构工具应该*了解docstrings.谁能评论他们的经历?

9> Greg Hewgill..:

看看模块的assertRaises方法unittest.



10> Daryl Spitze..:

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

from testcase import TestCase

import mymod

class MyTestCase(TestCase):
    def test1(self):
        self.assertRaisesWithMessage(SomeCoolException,
                                     'expected message',
                                     mymod.myfunc)

推荐阅读
手机用户2502851955
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有