Python unittest – Gegenteil von assertRaises?

Lesezeit: 11 Minuten

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

Es ist einfach zu testen, ob es sich um eine Exception handelt ist erzogen …

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

…aber wie kann man das machen Gegenteil.

So etwas in der Art, was ich suche …

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

  • Sie können immer einfach tun, was im Test funktionieren soll. Wenn es einen Fehler auslöst, wird dieser angezeigt (als Fehler und nicht als Fehler gezählt). Das setzt natürlich voraus, dass es keinen Fehler auslöst, sondern nur einen definierten Fehlertyp. Ansonsten müsstest du wohl selbst schreiben.

    –Thomas K

    30. November 2010 um 23:40 Uhr

  • mögliches Duplikat von Python – Test, der erfolgreich ist, wenn keine Ausnahme ausgelöst wird

    – brandizzi

    14. Juli 2015 um 1:22 Uhr

  • Es stellt sich heraus, dass Sie tatsächlich eine implementieren können assertNotRaises Methode, die 90 % ihres Codes/Verhaltens mit teilt assertRaises in ungefähr ~ 30 Codezeilen. Siehe meine Antwort unten für Details.

    – tel

    2. März 2018 um 4:47 Uhr


  • Ich möchte dies, damit ich zwei Funktionen mit vergleichen kann hypothesis um sicherzustellen, dass sie für alle Arten von Eingaben dieselbe Ausgabe erzeugen, während die Fälle ignoriert werden, in denen das Original eine Ausnahme auslöst. assume(func(a)) funktioniert nicht, da die Ausgabe ein Array mit mehrdeutigem Wahrheitswert sein kann. Also möchte ich nur eine Funktion aufrufen und bekommen True wenn es nicht scheitert. assume(func(a) is not None) funktioniert denke ich

    – Endolith

    19. April 2019 um 20:25 Uhr


  • Beantwortet das deine Frage? Bestehen Sie einen Python-Einheitentest, wenn keine Ausnahme ausgelöst wird

    – Flimmer

    6. Juli 2020 um 9:39 Uhr

Benutzer-Avatar
DGH

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

  • @hiwaylon – Nein, das ist tatsächlich die richtige Lösung. Die von user9876 vorgeschlagene Lösung ist konzeptionell fehlerhaft: Wenn Sie auf das Nichterhöhen von say testen ValueErroraber ValueError stattdessen ausgelöst wird, muss Ihr Test mit einer Fehlerbedingung beendet werden, nicht mit einer Fehlerbedingung. Wenn Sie andererseits beim Ausführen desselben Codes a auslösen würden KeyError, das wäre ein Fehler, kein Fehler. In Python werden – anders als in einigen anderen Sprachen – Ausnahmen routinemäßig zur Ablaufsteuerung verwendet, deshalb haben wir die except <ExceptionName> Syntax in der Tat. In dieser Hinsicht ist die Lösung von user9876 einfach falsch.

    – Mac

    18. April 2013 um 11:52 Uhr

  • Dieser hat den unglücklichen Effekt, dass für Tests eine Abdeckung von < 100 % angezeigt wird (die Ausnahme wird nie passieren).

    – Shay

    27. November 2018 um 20:10 Uhr

  • @Shay, IMO sollten Sie die Testdateien selbst immer von den Abdeckungsberichten ausschließen (da sie per Definition fast immer zu 100% ausgeführt werden, würden Sie die Berichte künstlich aufblasen).

    – Original BBQ-Sauce

    16. Oktober 2019 um 10:55 Uhr

  • @original-bbq-sauce, würde mich das nicht für unbeabsichtigt übersprungene Tests öffnen. ZB Tippfehler im Testnamen (ttst_function), falsche Laufkonfiguration in pycharm, etc.?

    – Shay

    19. Oktober 2019 um 15:14 Uhr

  • Das Erfordernis einer 100-prozentigen Abdeckung von Testdateien hat mehr als ein paar Fehler für mich entdeckt, die sonst wahrscheinlich nicht gefunden worden wären.

    – Katy Lavallee

    4. Februar 2021 um 16:10 Uhr

Hallo – ich möchte einen Test schreiben, um festzustellen, dass eine Ausnahme unter bestimmten Umständen nicht ausgelöst wird.

Das ist die Standardannahme – Ausnahmen werden nicht ausgelöst.

Wenn Sie nichts anderes sagen, wird dies in jedem einzelnen Test vorausgesetzt.

Dafür muss man eigentlich keine Behauptung schreiben.

  • @IndradhanushGupta Nun, die akzeptierte Antwort macht den Test pythonischer als diesen. Explizit ist besser als implizit.

    – 0xc0de

    29. Oktober 2015 um 7:14 Uhr


  • Kein anderer Kommentator hat darauf hingewiesen warum Diese Antwort ist falsch, obwohl es der gleiche Grund ist, aus dem die Antwort von user9876 falsch ist: Ausfälle und Fehler sind verschiedene Dinge im Testcode. Wenn Ihre Funktion war um eine Ausnahme während eines Tests auszulösen, der keine Assertion ausführt, würde das Test-Framework dies als eine behandeln Erroreher als ein Versäumnis, nicht zu behaupten.

    – Coredump-Fehler

    18. März 2016 um 22:02 Uhr

  • @CoreDumpError Ich verstehe den Unterschied zwischen einem Fehler und einem Fehler, aber würde Sie das nicht zwingen, ihn zu umgeben jede Prüfung mit einer Try/Exception-Konstruktion? Oder würden Sie empfehlen, dies nur für Tests zu tun, die unter bestimmten Bedingungen explizit eine Ausnahme auslösen (was im Grunde bedeutet, dass die Ausnahme erwartet).

    – federicojasson

    9. Januar 2018 um 13:23 Uhr


  • @federicojasson Sie haben Ihre eigene Frage in diesem zweiten Satz ganz gut beantwortet. Fehler vs. Fehler in Tests können kurz und bündig als “unerwartete Abstürze” vs. “unbeabsichtigtes Verhalten” beschrieben werden. Sie möchten, dass Ihre Tests einen Fehler anzeigen, wenn Ihre Funktion abstürzt, aber nicht, wenn eine Ausnahme, von der Sie wissen, dass sie bei bestimmten Eingaben ausgelöst wird, bei verschiedenen Eingaben ausgelöst wird.

    – Coredump-Fehler

    9. Januar 2018 um 18:41 Uhr

Benutzer-Avatar
Benutzer9876

Rufen Sie einfach die Funktion auf. Wenn es eine Ausnahme auslöst, kennzeichnet das Unit-Test-Framework dies als Fehler. Vielleicht möchten Sie einen Kommentar hinzufügen, z.

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

Bearbeitet, um Klarstellungen aus den Kommentaren hinzuzufügen:

  • Unit-Tests können 3 Ergebnisse haben: Pass, Fail, Error. (Eigentlich mehr, wenn man XPass/XFail/Skip mitzählt…)
  • Wenn Sie testen, dass eine bestimmte Ausnahme nicht ausgelöst wird, und sie ausgelöst wird, sollte dies theoretisch ein Fehler sein. Aber der obige Code macht es zu einem Fehler, der theoretisch “falsch” ist.
  • In der Praxis wird Ihr Testrunner bei einem Fehler wahrscheinlich den Stack-Trace drucken, was beim Debuggen des Fehlers nützlich sein kann. Bei einem Fail sehen Sie den Stack-Trace wahrscheinlich nicht.
  • Praktischerweise können Sie mit einem Nichtbestehen den Test als “Erwartet fehlschlagen” markieren. Mit einem Fehler können Sie das wahrscheinlich nicht tun, obwohl Sie den Test als “Überspringen” markieren können.
  • Aus praktischen Gründen ist zusätzlicher Code erforderlich, damit der Testfall einen Fehler meldet.
  • Ob der Unterschied zwischen „Error“ und „Failure“ eine Rolle spielt, hängt von Ihren Prozessen ab. So wie mein Team Einheitentests verwendet, müssen sie alle bestehen. (Agile Programmierung mit einer Continuous-Integration-Maschine, die alle Unit-Tests durchführt). Was für mein Team wirklich zählt, ist: „Bestehen alle Unit-Tests?“ (dh “ist Jenkins grün?”). Für mein Team gibt es also keinen praktischen Unterschied zwischen „Fail“ und „Error“.
  • Aufgrund der oben genannten Vorteile (weniger Code, Anzeigen des Stack-Trace) und der Tatsache, dass Fail/Error von meinem Team gleich behandelt werden, verwende ich diesen Ansatz.
  • Möglicherweise haben Sie andere Anforderungen, wenn Sie Ihre Komponententests anders verwenden, insbesondere wenn Ihre Prozesse „Fail“ und „Error“ unterschiedlich behandeln oder wenn Sie in der Lage sein möchten, Tests als „erwarteter Fehler“ zu markieren.
  • Wenn Sie diesen Test lieber als Fehler melden möchten, verwenden Sie die Antwort von DGH.

  • Ausfälle und Fehler sind konzeptionell unterschiedlich. Da in Python Ausnahmen routinemäßig für den Kontrollfluss verwendet werden, ist es außerdem sehr, sehr schwierig, auf einen Blick (= ohne den Testcode zu untersuchen) zu verstehen, ob Sie Ihre Logik oder Ihren Code beschädigt haben …

    – Mac

    29. November 2012 um 15:52 Uhr

  • Entweder dein Test besteht oder er tut es nicht. Wenn es nicht geht, musst du es reparieren gehen. Ob es als “Ausfall” oder “Fehler” gemeldet wird, ist meist irrelevant. Es gibt einen Unterschied: Mit meiner Antwort sehen Sie den Stack-Trace, damit Sie sehen können, wo PathIsNotAValidOne geworfen wurde. Mit der akzeptierten Antwort haben Sie diese Informationen nicht, sodass das Debuggen schwieriger wird. (Unter der Annahme von Py2; nicht sicher, ob Py3 darin besser ist).

    – Benutzer9876

    30. Juli 2014 um 11:08 Uhr

  • @ user9876 – Nein. Die Testausgangsbedingungen sind 3 (bestanden/nicht bestanden/Fehler), nicht 2, wie Sie fälschlicherweise zu glauben scheinen. Der Unterschied zwischen Fehlern und Ausfällen ist beträchtlich, und sie so zu behandeln, als wären sie gleich, ist nur schlechte Programmierung. Wenn Sie mir nicht glauben, schauen Sie sich einfach um, wie Test Runner funktionieren und welche Entscheidungsbäume sie für Ausfälle und Fehler implementieren. Ein guter Ausgangspunkt für Python ist das xfail Dekorateur im Pytest.

    – Mac

    21. Oktober 2014 um 12:29 Uhr

  • Ich denke, es hängt davon ab, wie Sie Unit-Tests verwenden. So wie mein Team Einheitentests verwendet, müssen sie alle bestehen. (Agile Programmierung mit einer Continuous-Integration-Maschine, die alle Unit-Tests durchführt). Ich weiß, dass der Testfall “bestanden”, “nicht bestanden” oder “Fehler” melden kann. Aber auf hohem Niveau ist es für mein Team wirklich wichtig, “werden alle Komponententests bestanden?” (dh “ist Jenkins grün?”). Für mein Team gibt es also keinen praktischen Unterschied zwischen „Fail“ und „Error“. Möglicherweise haben Sie andere Anforderungen, wenn Sie Ihre Komponententests auf andere Weise verwenden.

    – Benutzer9876

    1. April 2016 um 0:18 Uhr

  • @ user9876 Der Unterschied zwischen ‘fail’ und ‘error’ ist der Unterschied zwischen “mein Assert ist fehlgeschlagen” und “mein Test kommt nicht einmal zum Assert”. Das ist für mich eine nützliche Unterscheidung während der Reparatur von Tests, aber ich denke, wie Sie sagen, nicht jedermanns Sache.

    – CS

    11. September 2018 um 16:09 Uhr

Ich bin der ursprüngliche Verfasser und habe die obige Antwort von DGH akzeptiert, ohne sie zuerst im Code verwendet zu haben.

Als ich es benutzte, wurde mir klar, dass es ein wenig Anpassung brauchte, um tatsächlich das zu tun, was ich wollte (um fair zu DGH zu sein, sagte er/sie “oder so ähnlich”!).

Ich dachte, es lohnt sich, den Tweak hier zum Nutzen anderer zu posten:

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

Was ich hier versuchen wollte, war sicherzustellen, dass bei einem Versuch, ein Anwendungsobjekt mit einem zweiten Argument aus Leerzeichen zu instanziieren, pySourceAidExceptions.PathIsNotAValidOne ausgelöst würde.

Ich glaube, dass die Verwendung des obigen Codes (basierend auf der Antwort von DGH) dies tun wird.

Benutzer-Avatar
Tel

Sie können definieren assertNotRaises durch die Wiederverwendung von etwa 90 % der ursprünglichen Implementierung von assertRaises in dem unittest Modul. Mit diesem Ansatz erhalten Sie am Ende eine assertNotRaises Methode, die sich abgesehen von ihrer umgekehrten Fehlerbedingung identisch verhält assertRaises.

TLDR und Live-Demo

Es erweist sich als überraschend einfach, ein hinzuzufügen assertNotRaises Methode zu unittest.TestCase (Ich habe ungefähr viermal so lange gebraucht, um diese Antwort zu schreiben, als den Code). Hier ist eine Live-Demo der assertNotRaises Methode in Aktion. Gerade wie assertRaiseskönnen Sie entweder ein Callable und Argumente an übergeben assertNotRaisesoder Sie können es in a verwenden with Aussage. Die Live-Demo enthält einen Testfall, der dies demonstriert assertNotRaises funktioniert wie vorgesehen.

Einzelheiten

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

assertRaises ist eine kurze Methode, die im Grunde nur eine Instanz von erstellt unittest.case._AssertRaisesContext Klasse und gibt sie zurück (siehe ihre Definition in der unittest.case Modul). Sie können Ihre eigenen definieren _AssertNotRaisesContext Klasse durch Unterklassen _AssertRaisesContext und überschreibt seine __exit__ Methode:

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 erben lassen TestCase. 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

Alle Ihre Testfälle haben jetzt die assertNotRaises Methode, die ihnen zur Verfügung steht.

  • IMNSHO sollte dies der Unittest-Bibliothek hinzugefügt werden.

    – qneill

    30. Juli 2020 um 19:02 Uhr

Benutzer-Avatar
AndyJost

Ich fand es nützlich, Monkey-Patch zu machen unittest folgendermaßen:

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, was ich oft tue:

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

  • IMNSHO sollte dies der Unittest-Bibliothek hinzugefügt werden.

    – qneill

    30. Juli 2020 um 19:02 Uhr

Benutzer-Avatar
znottot

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.

anrufen wie

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

1111570cookie-checkPython unittest – Gegenteil von assertRaises?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy