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

测试是否引发异常是很简单的…

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

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

像这样的东西是我所追求的…

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

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

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

编辑以补充评论中的澄清:

Unit tests can have 3 results: Pass, Fail, Error. (Actually more if you count XPass/XFail/Skip...) If you're testing a particular exception is not thrown, and it is thrown, then in theory that should be a Fail. But the code above makes it an Error, which is theoretically "wrong". As a practical matter, with an Error your test runner will probably print the stack trace, which may be useful debugging the failure. With a Fail you probably won't see the stack trace. As a practical matter, with a Fail you can mark the test as "Expected to Fail". With an Error you probably can't do that, although you can mark the test as "skip". As a practical matter, making the test case report an Error requires additional code. Whether the difference between "Error" and "Failure" matters depends on your processes. The way my team uses unit tests, they have to all pass. (Agile programming, with a continuous integration machine that runs all the unit tests). What actually matters to my team is "do all the unit tests pass?" (i.e. "is Jenkins green?"). So for my team, there's no practical difference between "Fail" and "Error". Due to the advantages mentioned above (less code, seeing the stack trace), and the fact that Fail/Error are treated the same by my team, I use this approach. You may have different requirements if you use your unit tests in a different way, especially if your processes treat "fail" and "error" differently, or if you want to be able to mark tests as "expected failure". If you would rather have this test report an Error, use DGH's answer.


def run_test(self):
    try:
        myFunc()
    except ExceptionType:
        self.fail("myFunc() raised ExceptionType unexpectedly!")

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

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

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

你不需要为它写任何断言。


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

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

为了其他人的利益,我认为有必要在这里发布这个调整:

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

我在这里试图做的是确保如果尝试实例化一个应用程序对象,使用第二个空间参数pySourceAidExceptions。PathIsNotAValidOne将被引发。

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


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

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

这允许您测试代码中的错误用例。

在本例中,您正在测试将无效参数传递给应用程序构造函数时引发PathIsNotAValidOne。


我发现monkey-patch 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))

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

TLDR和现场演示

在unittest中添加assertNotRaises方法非常简单。TestCase(写这个答案的时间是写代码的4倍)。下面是assertNotRaises方法的现场演示。就像assertRaises一样,你可以传递一个可调用对象和参数给assertNotRaises,或者你可以在with语句中使用它。现场演示包括一个测试用例,演示assertNotRaises按预期工作。

细节

在unittest中实现assertRaises是相当复杂的,但是通过一些聪明的子类化,你可以覆盖和逆转它的失败条件。

assertRaises是一个简短的方法,基本上只创建unittest.case的一个实例。_AssertRaisesContext类并返回它(参见unittest. context中的定义)。模块)。你可以通过继承_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方法可用。


你可以试试。 试一试: 自我。assertRaises(没有,函数,__arg1、最长) 除了: 通过 如果你不把代码放在try块中,它会通过异常“AssertionError: None not raised”,测试用例将失败。如果将测试用例放入try块中,则测试用例将通过,这是预期的行为。


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

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

这里有一个例子:

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