mitmario.dev

Fehler erwarten

Python Sandbox 3 Min Lesezeit 4 BeispieleLektion 4 von 6

Über 31500 Gramm gibt porto() keinen Preis zurück. Stattdessen kommt ein ValueError mit der Meldung Zu schwer, das geht nur als Spedition, so wie in Abschnitt 9. Für diesen vierten Fall gibt es bisher keinen Test.

Ein Test dafür dreht die gewohnte Regel um. Sonst macht ein Fehler in einer Testfunktion den Test rot. Hier soll er ihn grün machen, aber nur, wenn er auch wirklich kommt.

Ein Fehler, der kommen soll

Ein Fehler, der kommen soll
import pytest

from porto import porto


def test_schwerstes_paket():
    assert porto(31500) == 9.99


def test_zu_schwer():
    with pytest.raises(ValueError, match="Spedition"):
        porto(31501)

Dafür gibt es in pytest pytest.raises. Es steht hinter einem with, das du vom Öffnen einer Datei kennst. Darunter kommt eingerückt der Aufruf, der den Fehler auslösen soll. Endet der Block mit einem ValueError, fängt pytest.raises ihn ab, und der Test ist grün.

Die beiden Tests sind das Grenzpaar aus der letzten Lektion, diesmal für die letzte Grenze. 31500 Gramm sind das schwerste Paket, das noch etwas kostet, 31501 Gramm das erste, das abgelehnt wird.

Mit match="Spedition" wird zusätzlich die Meldung geprüft. Der Text muss irgendwo darin vorkommen. Genau genommen ist er ein Suchmuster, in dem ein paar Zeichen eine eigene Bedeutung haben, ein Punkt etwa passt dort auf jedes beliebige Zeichen. Für Meldungen wie diese ist das egal.

Der Fehler bleibt aus

Der Fehler bleibt aus
import pytest

from porto import porto


def test_schwerstes_paket():
    assert porto(31500) == 9.99


def test_zu_schwer():
    with pytest.raises(ValueError, match="Spedition"):
        porto(31501)

Jemand hat porto() vereinfacht. Alles über 10 kg kostet jetzt 9,99 Euro, und die Ablehnung ist weg. Der Test für 31500 Gramm bleibt grün, die kosten ja wirklich 9,99 Euro. Rot wird nur der Test für 31501 Gramm, mit Failed: DID NOT RAISE ValueError. Der Fehler ist also nicht gekommen.

Ohne diesen Test würde der Shop jetzt ein Paket mit 40 kg für 9,99 Euro annehmen, und niemand würde es merken. Deshalb ist ein Test für den Fehlerfall mindestens so viel wert wie einer für den Normalfall. Wenn eine Ablehnung wegfällt, macht das keinen Lärm. Das Programm tut danach einfach etwas, das es nicht tun sollte, und meldet dabei keinen Fehler.

Die falsche Meldung

Die falsche Meldung
import pytest

from porto import porto


def test_schwerstes_paket():
    assert porto(31500) == 9.99


def test_zu_schwer():
    with pytest.raises(ValueError, match="Spedition"):
        porto(31501)

Hier lehnt porto() noch ab, mit dem richtigen ValueError, aber mit einer neuen Meldung. Von der Spedition ist darin keine Rede mehr. Rot wird der Test trotzdem, wegen match.

pytest schreibt Regex pattern did not match., das Suchmuster hat also nicht gepasst. Regex ist die Kurzform von regulärer Ausdruck, so heißen solche Muster. Darunter stehen das Muster und die Meldung, die wirklich kam, Expected regex: 'Spedition' und Actual message: 'Zu schwer für ein Paket'.

Ob die Meldung wichtig genug für einen Test ist, hängt davon ab, wer sie liest. Wenn die Kundin sie im Shop zu sehen bekommt, gehört der Hinweis auf die Spedition zum Ergebnis wie der Preis. Dann gehört er auch in den Test.

Irgendein Fehler

Irgendein Fehler
import pytest

from porto import porto


def test_zu_schwer_irgendein_fehler():
    with pytest.raises(Exception):
        porto(31501)


def test_zu_schwer_valueerror():
    with pytest.raises(ValueError):
        porto(31501)

Der Fehler dieser Lektion ist ein Tippfehler in porto(). Da steht ValueEror statt ValueError, und deshalb löst Python einen NameError aus statt der Ablehnung.

Der erste Test erwartet nur Exception, und der ist grün. Exception ist der Oberbegriff für fast alle Fehlerarten, auch für NameError. Das kennst du aus Lektion 9.2 von except Exception. Der Test ist also mit jedem Fehler zufrieden, auch mit diesem.

Der zweite Test erwartet einen ValueError und wird rot. pytest zeigt dabei den ganzen Weg, erst die Zeile im Test, dann die Zeile in porto.py und darunter NameError: name 'ValueEror' is not defined. Wenn ein Test genau die Fehlerart erwartet, die die Funktion verspricht, heißt sein Grün auch, dass die Funktion sich daran hält.

Was die Aufgabe will

ueberweise() aus Lektion 9.4 lehnt zwei Dinge mit einem ValueError ab. Einen Betrag, der nicht größer als null ist, und einen, der größer ist als der Kontostand. Du schreibst Tests für eine Überweisung, die klappt, und für beide Ablehnungen samt Meldung. Danach baut pruefen.py Fehler in die Funktion ein, auch solche, die nur ein Test mit match bemerkt.

Zum Mitnehmen

Mit with pytest.raises(ValueError) erwartet ein Test, dass der Code darunter genau diese Fehlerart auslöst, und er wird rot, wenn sie ausbleibt. Mit match= kommt dazu, dass ein bestimmter Text in der Meldung stehen muss. Ein Test, der nur Exception erwartet, nimmt jeden Fehler hin, auch einen Tippfehler.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 4 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.