繁体   English   中英

Python unittest - 与 assertRaises 相对?

[英]Python unittest - opposite of assertRaises?

我想编写一个测试来确定在给定情况下不会引发异常。

测试是否引发异常很简单......

sInvalidPath=AlwaysSuppliesAnInvalidPath()
self.assertRaises(PathIsNotAValidOne, MyObject, sInvalidPath) 

...但你怎么能反其道而行之

这样的事情我就是我所追求的......

sValidPath=AlwaysSuppliesAValidPath()
self.assertNotRaises(PathIsNotAValidOne, MyObject, sValidPath) 
def run_test(self):
    try:
        myFunc()
    except ExceptionType:
        self.fail("myFunc() raised ExceptionType unexpectedly!")

嗨 - 我想编写一个测试来确定在给定情况下不会引发异常。

这是默认假设——不会引发异常。

如果你什么都不说,那在每一个测试中都是假设的。

您实际上不必为此编写任何断言。

只需调用该函数。 如果它引发异常,单元测试框架会将其标记为错误。 您可能想添加评论,例如:

sValidPath=AlwaysSuppliesAValidPath()
# Check PathIsNotAValidOne not thrown
MyObject(sValidPath)

编辑以添加评论中的说明:

  • 单元测试可以有 3 个结果:通过、失败、错误。 (实际上更多,如果你算上 XPass/XFail/Skip...)
  • 如果你正在测试一个特定的异常没有被抛出,而是被抛出,那么理论上应该是一个失败。 但是上面的代码使它成为一个错误,这在理论上是“错误的”。
  • 实际上,如果出现错误,您的测试运行程序可能会打印堆栈跟踪,这可能有助于调试故障。 如果失败,您可能看不到堆栈跟踪。
  • 实际上,如果失败,您可以将测试标记为“预期失败”。 对于错误,您可能无法做到这一点,尽管您可以将测试标记为“跳过”。
  • 实际上,使测试用例报告错误需要额外的代码。
  • “错误”和“失败”之间的区别是否重要取决于您的流程。 我的团队使用单元测试的方式,他们必须全部通过。 (敏捷编程,使用持续集成机器运行所有单元测试)。 对我的团队来说真正重要的是“所有的单元测试都通过了吗?” (即“詹金斯是绿色的吗?”)。 所以对于我的团队来说,“失败”和“错误”之间没有实际区别。
  • 由于上面提到的优点(更少的代码,查看堆栈跟踪),以及我的团队对失败/错误的处理相同的事实,我使用这种方法。
  • 如果您以不同的方式使用单元测试,您可能会有不同的要求,特别是如果您的流程以不同的方式对待“失败”和“错误”,或者如果您希望能够将测试标记为“预期失败”。
  • 如果您希望此测试报告错误,请使用 DGH 的答案。

我是原始海报,我接受了 DGH 的上述答案,而没有首先在代码中使用它。

一旦我确实使用了,我意识到它需要稍微调整才能真正做我需要它做的事情(公平地说,他/她确实说过“或类似的东西”!)。

我认为值得在此处发布调整以造福他人:

    try:
        a = Application("abcdef", "")
    except pySourceAidExceptions.PathIsNotAValidOne:
        pass
    except:
        self.assertTrue(False)

我在这里尝试做的是确保如果尝试使用第二个空格参数实例化 Application 对象,则会引发 pySourceAidExceptions.PathIsNotAValidOne 。

我相信使用上面的代码(主要基于 DGH 的答案)会做到这一点。

您可以通过在unittest模块中重用大约 90% 的assertRaises原始实现来定义assertNotRaises 使用这种方法,您最终会得到一个assertNotRaises方法,除了其反向失败条件外,其行为与assertRaises相同。

TLDR 和现场演示

事实证明,将assertNotRaises方法添加到unittest.TestCase非常容易(我编写这个答案的时间是编写代码的时间的 4 倍)。 这是assertNotRaises方法的现场演示 就像assertRaises一样,您可以将 callable 和 args 传递给assertNotRaises ,也可以在with语句中使用它。 现场演示包括一个测试用例,演示了assertNotRaises按预期工作。

细节

unittestassertRaises的实现相当复杂,但是通过一些巧妙的子类化,您可以覆盖和逆转其失败条件。

assertRaises是一个简短的方法,它基本上只是创建unittest.case._AssertRaisesContext类的一个实例并返回它(参见它在unittest.case模块中的定义)。 您可以通过子类化_AssertRaisesContext并覆盖其__exit__方法来定义自己的_AssertNotRaisesContext类:

import traceback
from unittest.case import _AssertRaisesContext

class _AssertNotRaisesContext(_AssertRaisesContext):
    def __exit__(self, exc_type, exc_value, tb):
        if exc_type is not None:
            self.exception = exc_value.with_traceback(None)

            try:
                exc_name = self.expected.__name__
            except AttributeError:
                exc_name = str(self.expected)

            if self.obj_name:
                self._raiseFailure("{} raised by {}".format(exc_name,
                    self.obj_name))
            else:
                self._raiseFailure("{} raised".format(exc_name))

        else:
            traceback.clear_frames(tb)

        return True

通常,您通过让它们从TestCase继承来定义测试用例类。 如果您改为从子类MyTestCase继承:

class MyTestCase(unittest.TestCase):
    def assertNotRaises(self, expected_exception, *args, **kwargs):
        context = _AssertNotRaisesContext(expected_exception, self)
        try:
            return context.handle('assertNotRaises', args, kwargs)
        finally:
            context = None

您的所有测试用例现在都可以使用assertNotRaises方法。

def _assertNotRaises(self, exception, obj, attr):                                                                                                                              
     try:                                                                                                                                                                       
         result = getattr(obj, attr)                                                                                                                                            
         if hasattr(result, '__call__'):                                                                                                                                        
             result()                                                                                                                                                           
     except Exception as e:                                                                                                                                                     
         if isinstance(e, exception):                                                                                                                                           
            raise AssertionError('{}.{} raises {}.'.format(obj, attr, exception)) 

如果需要接受参数可以修改。

呼叫喜欢

self._assertNotRaises(IndexError, array, 'sort')

我发现它是有用的猴子补丁unittest如下:

def assertMayRaise(self, exception, expr):
  if exception is None:
    try:
      expr()
    except:
      info = sys.exc_info()
      self.fail('%s raised' % repr(info[0]))
  else:
    self.assertRaises(exception, expr)

unittest.TestCase.assertMayRaise = assertMayRaise

这在测试是否存在异常时阐明了意图:

self.assertMayRaise(None, does_not_raise)

这也简化了循环中的测试,我经常发现自己在这样做:

# ValueError is raised only for op(x,x), op(y,y) and op(z,z).
for i,(a,b) in enumerate(itertools.product([x,y,z], [x,y,z])):
  self.assertMayRaise(None if i%4 else ValueError, lambda: op(a, b))

你可以这样试试。 try: self.assertRaises(None,function,arg1, arg2) except: pass 如果你不把代码放在 try 块中,它将通过异常' AssertionError: None not raise "并且测试用例将失败。测试用例将通过如果放在 try 块中,这是预期的行为。

如果您将 Exception 类传递给assertRaises() ,则会提供上下文管理器。 这可以提高测试的可读性:

# raise exception if Application created with bad data
with self.assertRaises(pySourceAidExceptions.PathIsNotAValidOne):
    application = Application("abcdef", "")

这允许您测试代码中的错误情况。

在这种情况下,您正在测试PathIsNotAValidOne是否在您将无效参数传递给 Application 构造函数时引发。

确保对象初始化没有任何错误的一种直接方法是测试对象的类型实例。

这是一个例子:

p = SomeClass(param1=_param1_value)
self.assertTrue(isinstance(p, SomeClass))

我想编写一个测试来确定在给定的情况下不引发异常。

测试是否引发异常很简单...

sInvalidPath=AlwaysSuppliesAnInvalidPath()
self.assertRaises(PathIsNotAValidOne, MyObject, sInvalidPath) 

...但是你怎么做相反

像这样的东西我在追求...

sValidPath=AlwaysSuppliesAValidPath()
self.assertNotRaises(PathIsNotAValidOne, MyObject, sValidPath) 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM