Abschnitt 15 · Lektion 2
pytest
Eine Datei voller assert hört beim ersten Fehler auf. Was danach kommt, läuft nicht mehr. Und an
der Zeile, die anschlägt, steht nur AssertionError, aber nicht, was stattdessen herauskam. Beides
macht pytest besser, ein Paket von PyPI und das Werkzeug, mit dem dieser Abschnitt arbeitet. Hier
liegt es bereit. Zu Hause installierst du es mit python -m pip install pytest, am besten in der
Umgebung des Projekts (Lektion 13.3).
Tests als Funktionen
def porto(gramm):
"""Was ein Paket kostet, nach seinem Gewicht in Gramm."""
if gramm <= 2000:
return 3.99
elif gramm <= 10000:
return 5.99
elif gramm <= 31500:
return 9.99
else:
raise ValueError("Zu schwer, das geht nur als Spedition") # Startet pytest, wie python -m pytest -q es im Terminal tut.
import pytest
pytest.main(["-q"]) from porto import porto
def test_leichtes_paeckchen():
assert porto(500) == 3.99
def test_paeckchen_an_der_grenze():
assert porto(2000) == 3.99
def test_paket_knapp_darueber():
assert porto(2001) == 5.99
def test_grosses_paket():
assert porto(12000) == 9.99 Das sind dieselben vier assert-Zeilen wie in der letzten Lektion, nur steht jetzt jede in einer
eigenen Funktion. Ihr Name fängt mit test_ an, und die Datei, in der sie stehen, heißt ebenfalls
test_ und dann irgendwas. Mehr braucht es nicht, auch wenn pytest noch ein paar andere
Schreibweisen kennt, etwa porto_test.py.
pytest sucht im Ordner nach solchen Dateien, liest sie ein und ruft jede Funktion mit test_ vorn
auf, jede für sich. Aufrufen musst du sie nicht. Innen steht ein ganz gewöhnliches assert.
Die Ausgabe ist kurz. Ein Punkt je grünem Test, ganz rechts [100%], darunter 4 passed. Die Zeit
dahinter ist bei jedem Lauf eine andere. Der Name eines Tests darf ruhig lang sein und sagen,
welcher Fall geprüft wird. Wenn der Test einmal rot wird, liest du ihn als Erstes.
Zu Hause startest du pytest im Ordner des Projekts mit python -m pytest -q. Dieser Befehl steht
hier auch in der Befehlsleiste. -q (quiet, leise) lässt oben die Angaben zu Python und pytest weg.
Mit python -m pytest ohne -q, ebenfalls in der Befehlsleiste, siehst du sie. python -m steht
aus demselben Grund davor wie bei pip in Lektion 13.1, so nimmt man das pytest, das zu diesem Python
gehört.
Bei einem Beispiel wird hier immer eine Datei gestartet. Deshalb liegt daneben eine kleine
pruefen.py, die mit pytest.main(["-q"]) dasselbe tut wie der Befehl. In den Aufgaben startet
pruefen.py pytest genauso und macht danach noch etwas mehr.
Ein Test wird rot
def porto(gramm):
"""Was ein Paket kostet, nach seinem Gewicht in Gramm."""
if gramm < 2000:
return 3.99
elif gramm <= 10000:
return 5.99
elif gramm <= 31500:
return 9.99
else:
raise ValueError("Zu schwer, das geht nur als Spedition") # Startet pytest, wie python -m pytest -q es im Terminal tut.
import pytest
pytest.main(["-q"]) from porto import porto
def test_leichtes_paeckchen():
assert porto(500) == 3.99
def test_paeckchen_an_der_grenze():
assert porto(2000) == 3.99
def test_paket_knapp_darueber():
assert porto(2001) == 5.99
def test_grosses_paket():
assert porto(12000) == 9.99 Der Fehler aus der letzten Lektion ist wieder drin, aus <= wurde <. In der ersten Zeile steht
.F... Der zweite Test ist rot, F für failed, und die beiden danach sind trotzdem gelaufen.
Darunter, unter FAILURES, kommt der rote Test im Einzelnen. Die Zeile mit dem > davor ist die,
an der es passiert ist. In den Zeilen mit E steht, was los war: assert 5.99 == 3.99, und
darunter where 5.99 = porto(2000). Herausgekommen sind 5.99, erwartet waren 3.99, und die 5.99
stammen aus dem Aufruf porto(2000).
Dafür musst du nichts tun. pytest sieht sich jedes assert einer Testdatei an, bevor es sie laufen
lässt, und merkt sich die Werte auf beiden Seiten.
Ganz unten steht in einer Zeile, was rot ist, mit dem Namen der Datei und des Tests. Die letzte
Zeile zählt: 1 failed, 3 passed. Außerdem endet pytest mit einem Exit-Code, den
python -m pytest -q; echo $? aus der Befehlsleiste zeigt. Er ist 0, wenn alles grün ist, 1, wenn
mindestens ein Test rot ist, und 5, wenn pytest gar keinen Test gefunden hat.
Im letzten Fall steht dort nur no tests ran. Das passiert, wenn die Namen nicht passen, denn eine
Funktion pruefe_porto() ist für pytest eine Funktion wie jede andere. Die Aufgabe dieser Lektion
fängt so an, mit einer Datei ohne Test.
Ein Test ohne assert
def porto(gramm):
"""Was ein Paket kostet, nach seinem Gewicht in Gramm."""
if gramm < 2000:
return 3.99
elif gramm <= 10000:
return 5.99
elif gramm <= 31500:
return 9.99
else:
raise ValueError("Zu schwer, das geht nur als Spedition") # Startet pytest, wie python -m pytest -q es im Terminal tut.
import pytest
pytest.main(["-q"]) from porto import porto
def test_leichtes_paeckchen():
assert porto(500) == 3.99
def test_paeckchen_an_der_grenze():
porto(2000) == 3.99
def test_paket_knapp_darueber():
assert porto(2001) == 5.99
def test_grosses_paket():
assert porto(12000) == 9.99 Derselbe Fehler steckt in porto(), und trotzdem steht da 4 passed. Im zweiten Test fehlt das
assert. Die Zeile porto(2000) == 3.99 wird zwar ausgerechnet, und das Ergebnis ist False. Aber
es geht nirgends hin, so wie bei einer Zeile, in der nur 3 + 4 steht. Für pytest ist ein Test
grün, wenn die Funktion ohne Fehler zu Ende läuft. Und das tut sie.
Das ist der Fehler dieser Lektion. Er ist gemeiner als die Klammern hinter assert, weil hier nicht
einmal Python warnt. Ein Test ohne assert prüft nichts und steht trotzdem genauso grün in der
Liste wie einer, der etwas prüft. Auffallen tut er nur auf die Art aus der letzten Lektion, wenn du
einen Fehler einbaust und der Test trotzdem grün bleibt.
Was die Aufgabe will
Zwei Tests für eine Funktion rabatt(), die einen Preis um ein paar Prozent senkt. pruefen.py
startet pytest. Wenn alles grün ist, werden nacheinander zwei Fehler in die Funktion eingebaut, und
bei jedem muss einer deiner Tests rot werden.
Zum Mitnehmen
pytest sammelt Dateien und Funktionen ein, deren Name mit test_ beginnt, und führt jede Funktion als eigenen Test aus. Darin reicht ein schlichtes assert, und wird ein Test rot, stehen beide Seiten des Vergleichs in der Ausgabe. Ohne assert prüft eine Testfunktion gar nichts und gilt trotzdem als passed.
Jetzt du
Basis Konto, kostenlosZu 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 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe, dein Code läuft auf einem Server
Öffnet sich mit dem Basis Konto.