webentwicklung-frage-antwort-db.com.de

Python unittest - Gegenteil von assertRaises?

Ich möchte einen Test schreiben, um festzustellen, dass unter bestimmten Umständen keine Exception ausgelöst wird. 

Es ist einfach zu testen, ob eine Ausnahme ist erhöht ist ...

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

... aber wie kann man das Gegenteil machen? 

So etwas habe ich, was ich suche ...

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

Hallo - Ich möchte einen Test schreiben, um festzustellen, dass unter bestimmten Umständen keine Exception ausgelöst wird.

Dies ist die Standardannahme - Ausnahmen werden nicht ausgelöst.

Wenn Sie nichts anderes sagen, wird dies bei jedem Test angenommen.

Sie müssen dafür keine Aussage machen. 

54
S.Lott

Rufen Sie einfach die Funktion auf. Wenn eine Ausnahme ausgelöst wird, wird das Gerätetest-Framework dies als Fehler kennzeichnen. Sie möchten vielleicht einen Kommentar hinzufügen, z.

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

Ich bin das Originalposter und habe die obige Antwort von DGH akzeptiert, ohne sie zuvor im Code verwendet zu haben. 

Als ich das Gerät einmal benutzt hatte, wurde mir klar, dass es ein wenig mehr nötig war, um das zu tun, was ich brauchte (um der DGH gerecht zu werden, sagte er/sie "oder etwas ähnliches"!). 

Ich dachte, es lohnt sich, den Tweak hier zu veröffentlichen, um anderen zu helfen:

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

Was ich hier tun wollte, war sicherzustellen, dass bei einem Versuch, ein Application-Objekt mit einem zweiten Argument von Leerzeichen zu instantiieren, pySourceAidExceptions.PathIsNotAValidOne ausgelöst wird. 

Ich glaube, dass der oben genannte Code (basierend auf der Antwort der DGH) dies tun wird.

13
glaucon

Sie können assertNotRaises definieren, indem Sie ca. 90% der ursprünglichen Implementierung von assertRaises im Modul unittest wiederverwenden. Bei diesem Ansatz erhalten Sie eine assertNotRaises-Methode, die sich abgesehen von ihrer umgekehrten Fehlerbedingung mit assertRaises identisch verhält.

TLDR und Live-Demo

Es stellt sich als überraschend einfach heraus, eine assertNotRaises-Methode zu unittest.TestCase hinzuzufügen (ich habe ungefähr viermal so lange gebraucht, um diese Antwort zu schreiben als der Code). Hier ist eine Live-Demo der assertNotRaises-Methode in Aktion . Sie können einfach wie assertRaises entweder ein callable und args an assertNotRaises übergeben, oder Sie können es in einer with-Anweisung verwenden. Die Live-Demo enthält Testfälle, die zeigen, dass assertNotRaises wie beabsichtigt funktioniert.

Einzelheiten

Die Implementierung von assertRaises in unittest ist ziemlich kompliziert, aber mit ein wenig kluger Unterklassifizierung können Sie die Fehlerbedingung überschreiben und umkehren.

assertRaises ist eine kurze Methode, die im Grunde nur eine Instanz der unittest.case._AssertRaisesContext-Klasse erstellt und zurückgibt (siehe Definition im unittest.case-Modul). Sie können Ihre eigene _AssertNotRaisesContext-Klasse definieren, indem Sie _AssertRaisesContext subclassing und die __exit__-Methode überschreiben:

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

Normalerweise definieren Sie Testfallklassen, indem Sie sie von TestCase erben lassen. Wenn Sie stattdessen von einer Unterklasse erben, 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

für alle Ihre Testfälle steht jetzt die assertNotRaises-Methode zur Verfügung.

4
tel

Wenn Sie eine Exception-Klasse an assertRaises() übergeben, wird ein Kontextmanager bereitgestellt. Dies kann die Lesbarkeit Ihrer Tests verbessern:

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

Auf diese Weise können Sie Fehlerfälle in Ihrem Code testen. 

In diesem Fall testen Sie, dass PathIsNotAValidOne ausgelöst wird, wenn Sie ungültige Parameter an den Anwendungskonstruktor übergeben.

1
hiwaylon

Ich habe es für nützlich gefunden, unittest wie folgt zu aktualisieren:

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

Dies verdeutlicht die Absicht beim Testen auf das Fehlen einer Ausnahme:

self.assertMayRaise(None, does_not_raise)

Dies vereinfacht auch das Testen in einer Schleife, die ich oft mache:

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

könnte geändert werden, wenn Sie Parameter akzeptieren müssen.

nennen wie

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

sie können es so versuchen. Versuchen: self.assertRaises (Keine, Funktion, Arg1, Arg2) außer: Pass Wenn Sie keinen Try-Block in den Try-Block einfügen, wird durch die Ausnahme 'AssertionError: None nicht ausgelöst' der Testfall fehlschlagen.

0
lalit