515 Stimmen

Python unittest - Gegenteil von assertRaises?

Ich möchte einen Test schreiben, um festzustellen, dass eine Exception in einer bestimmten Situation nicht ausgelöst wird.

Es ist ganz einfach zu testen, ob eine Exception ist angehoben ...

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

... aber wie können Sie die gegenüber .

Ich suche so etwas wie das hier ...

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

568voto

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

88voto

S.Lott Punkte 371691

Hallo - Ich möchte einen Test schreiben, um festzustellen, dass eine Exception nicht in einem bestimmten Umstand ausgelöst wird.

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

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

Sie müssen dafür keine Behauptung aufstellen.

58voto

user9876 Punkte 10656

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

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

Bearbeitet, um Klarstellungen aus den Kommentaren hinzuzufügen:

  • Einheitstests können 3 Ergebnisse haben: Bestanden, Fehlgeschlagen, Fehler. (Eigentlich mehr, wenn Sie XPass/XFail/Skip zählen...)
  • Wenn Sie testen, dass eine bestimmte Ausnahme nicht ausgelöst wird, und sie wird ausgelöst, dann sollte das theoretisch ein Fail sein. Aber der obige Code macht daraus einen Fehler, was theoretisch "falsch" ist.
  • In der Praxis wird Ihr Testrunner bei einem Fehler wahrscheinlich den Stack-Trace ausgeben, was bei der Fehlersuche nützlich sein kann. Bei einem Fail werden Sie den Stack-Trace wahrscheinlich nicht sehen.
  • Praktischerweise können Sie bei einem Fail den Test als "voraussichtlich nicht bestanden" markieren. Bei einem Fehler können Sie das wahrscheinlich nicht tun, obwohl Sie den Test als "überspringen" markieren können.
  • In der Praxis erfordert die Meldung eines Fehlers durch den Testfall zusätzlichen Code.
  • Ob der Unterschied zwischen "Error" und "Failure" von Bedeutung ist, hängt von Ihren Prozessen ab. So wie mein Team die Unit-Tests einsetzt, müssen sie alle bestehen. (Agile Programmierung, mit einer kontinuierlichen Integrationsmaschine, die alle Unit-Tests ausführt). Was für mein Team wirklich wichtig ist, ist: "Bestehen alle Unit-Tests?" (d. h. "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, Einsicht in den 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 Unit-Tests auf eine andere Art und Weise verwenden, insbesondere wenn Ihre Prozesse "Fehlschlag" und "Fehler" unterschiedlich behandeln oder wenn Sie in der Lage sein wollen, Tests als "erwarteter Fehlschlag" zu markieren.
  • Wenn Sie lieber möchten, dass der Test einen Fehler meldet, verwenden Sie die Antwort von DGH.

21voto

tel Punkte 12202

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

TLDR und Live-Demo

Es stellt sich heraus, dass es erstaunlich einfach ist, eine assertNotRaises Methode zu unittest.TestCase (Ich habe etwa 4 Mal so lange für diese Antwort gebraucht wie für den Code). Hier ist eine Live-Demo des assertNotRaises Methode in Aktion . Einfach wie assertRaises können Sie entweder ein Callable und Args an assertNotRaises oder Sie können es in einer with Erklärung. Die Live-Demo enthält einen Testfall, der zeigt, dass assertNotRaises funktioniert wie vorgesehen.

Einzelheiten

Die Umsetzung der assertRaises en unittest ist ziemlich kompliziert, aber mit ein wenig geschicktem Subclassing können Sie die Fehlerbedingung außer Kraft setzen und umkehren.

assertRaises ist eine kurze Methode, die im Grunde nur eine Instanz der unittest.case._AssertRaisesContext Klasse und gibt sie zurück (siehe ihre Definition in der unittest.case Modul). Sie können Ihre eigenen _AssertNotRaisesContext Klasse durch Unterklassifizierung _AssertRaisesContext und die Überschreibung seiner __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 von 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

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

18voto

glaucon Punkte 7568

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

Nachdem ich es benutzt hatte, stellte ich fest, dass es ein wenig optimiert werden musste, um das zu tun, was ich brauchte (um DGH gegenüber fair zu sein, sagte er/sie "oder etwas Ähnliches"!).

Ich dachte, es lohnt sich, die Änderung hier zu veröffentlichen, damit andere davon profitieren können:

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

Ich wollte damit sicherstellen, dass bei einem Versuch, ein Anwendungsobjekt mit einem zweiten Argument von Leerzeichen zu instanziieren, die pySourceAidExceptions.PathIsNotAValidOne ausgelöst wird.

Ich glaube, dass die Verwendung des obigen Codes (der sich stark an DGHs Antwort orientiert) dies ermöglicht.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X