Python单元测试-与assertRaises相反吗?

问题:Python单元测试-与assertRaises相反吗?

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

测试是否引发异常很简单

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

…但是你怎么做相反

像这样的东西我在追求…

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

I want to write a test to establish that an Exception is not raised in a given circumstance.

It’s straightforward to test if an Exception is raised …

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

… but how can you do the opposite.

Something like this i what I’m after …

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

回答 0

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

回答 1

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

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

如果您什么都没说,则在每个测试中都假设了这一点。

您不必为此写任何断言。

Hi – I want to write a test to establish that an Exception is not raised in a given circumstance.

That’s the default assumption — exceptions are not raised.

If you say nothing else, that’s assumed in every single test.

You don’t have to actually write an any assertion for that.


回答 2

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

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

Just call the function. If it raises an exception, the unit test framework will flag this as an error. You might like to add a comment, e.g.:

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

回答 3

我是原始发布者,我没有在代码中首先使用它就接受了DGH的上述回答。

一旦我使用完,我意识到实际上需要做一些调整才能做我需要做的事情(为了公平对待DGH,他/她确实说了“或类似的话!”)。

我认为值得在这里发表一些调整以使他人受益:

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

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

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

I am the original poster and I accepted the above answer by DGH without having first used it in the code.

Once I did use I realised that it needed a little tweaking to actually do what I needed it to do (to be fair to DGH he/she did say “or something similar” !).

I thought it was worth posting the tweak here for the benefit of others:

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

What I was attempting to do here was to ensure that if an attempt was made to instantiate an Application object with a second argument of spaces the pySourceAidExceptions.PathIsNotAValidOne would be raised.

I believe that using the above code (based heavily on DGH’s answer) will do that.


回答 4

您可以定义assertNotRaises通过重用对原执行的90%assertRaises的中unittest模块。使用这种方法,最终得到的assertNotRaises方法除了其反向失败条件外,其行为与相同assertRaises

TLDR和现场演示

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

细节

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

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

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可用的方法。

You can define assertNotRaises by reusing about 90% of the original implementation of assertRaises in the unittest module. With this approach, you end up with an assertNotRaises method that, aside from its reversed failure condition, behaves identically to assertRaises.

TLDR and live demo

It turns out to be surprisingly easy to add an assertNotRaises method to unittest.TestCase (it took me about 4 times as long to write this answer as it did the code). Here’s a live demo of the assertNotRaises method in action. Just like assertRaises, you can either pass a callable and args to assertNotRaises, or you can use it in a with statement. The live demo includes a test cases that demonstrates that assertNotRaises works as intended.

Details

The implementation of assertRaises in unittest is fairly complicated, but with a little bit of clever subclassing you can override and reverse its failure condition.

assertRaises is a short method that basically just creates an instance of the unittest.case._AssertRaisesContext class and returns it (see its definition in the unittest.case module). You can define your own _AssertNotRaisesContext class by subclassing _AssertRaisesContext and overriding its __exit__ method:

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

Normally you define test case classes by having them inherit from TestCase. If you instead inherit from a subclass 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

all of your test cases will now have the assertNotRaises method available to them.


回答 5

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

could be modified if you need to accept parameters.

call like

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

回答 6

我发现unittest按以下步骤进行Monkey补丁很有用:

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

I’ve found it useful to monkey-patch unittest as follows:

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

This clarifies intent when testing for the absence of an exception:

self.assertMayRaise(None, does_not_raise)

This also simplifies testing in a loop, which I often find myself doing:

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

回答 7

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

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

这使您可以在代码中测试错误情况。

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

If you pass an Exception class to assertRaises(), a context manager is provided. This can improve the readability of your tests:

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

This allows you to test error cases in your code.

In this case, you are testing the PathIsNotAValidOne is raised when you pass invalid parameters to the Application constructor.


回答 8

你可以这样尝试。尝试:self.assertRaises(None,function,arg1,arg2)除外:如果不将代码放入try块,则通过,它将通过异常“ AssertionError:未引发”,测试用例将失败。测试用例将通过如果放在try块中,这是预期的行为。

you can try like that. try: self.assertRaises(None,function,arg1, arg2) except: pass if you don’t put code inside try block it will through exception’ AssertionError: None not raised ” and test case will be failed. Test case will be pass if put inside try block which is expected behaviour.


回答 9

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

这是一个例子:

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

One straight forward way to ensure the object is initialized without any error is to test the object’s type instance.

Here is an example :

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