Kurz: Ein Programm, das heute läuft, kann morgen nach einer kleinen Änderung kaputt sein. Automatische Tests prüfen deinen Code auf Knopfdruck. Sie sind das Sicherheitsnetz, mit dem du ohne Angst ändern kannst.
Teil des Kurses Python
Ein Programm, das heute läuft, kann morgen nach einer kleinen Änderung kaputt sein. Automatische Tests prüfen deinen Code auf Knopfdruck. Sie sind das Sicherheitsnetz, mit dem du ohne Angst ändern kannst.
Die Idee
Du schreibst kleine Funktionen, die deinen Code mit bekannten Eingaben aufrufen und das Ergebnis mit der erwarteten Ausgabe vergleichen. Stimmt etwas nicht, schlägt der Test fehl.
Der einfachste Test ist assert:
def addiere(a, b):
return a + b
assert addiere(2, 3) == 5
assert addiere(-1, 1) == 0
print("alle Prüfungen bestanden")
try:
assert addiere(2, 2) == 5, "2 + 2 sollte 4 sein"
except AssertionError as e:
print("Fehler:", e)Ausgabe:
alle Prüfungen bestanden
Fehler: 2 + 2 sollte 4 seinunittest (Standardbibliothek)
import unittest
def ist_primzahl(n):
if n < 2:
return False
return all(n % t for t in range(2, int(n ** 0.5) + 1))
class TestPrimzahl(unittest.TestCase):
def test_primzahlen(self):
for p in (2, 3, 5, 7, 11):
self.assertTrue(ist_primzahl(p))
def test_keine_primzahlen(self):
for n in (0, 1, 4, 9, 15):
self.assertFalse(ist_primzahl(n))
def test_fehler(self):
with self.assertRaises(TypeError):
ist_primzahl("a")
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestPrimzahl)
ergebnis = unittest.TextTestRunner(verbosity=0, stream=open("/dev/null", "w")).run(suite)
print("Tests:", ergebnis.testsRun, "Fehler:", len(ergebnis.failures) + len(ergebnis.errors))Ausgabe:
Tests: 3 Fehler: 0In einer eigenen Datei test_irgendwas.py genügt am Ende:
if __name__ == "__main__":
unittest.main()Oder du startest alle Tests mit python -m unittest. Wichtige Prüfmethoden:
| Methode | Prüft |
|---|---|
assertEqual(a, b) | a == b |
assertTrue(x) / assertFalse(x) | Wahrheitswert |
assertIn(a, b) | a in b |
assertAlmostEqual(a, b) | fast gleich (Kommazahlen) |
assertRaises(Fehler) | löst die Ausnahme aus |
pytest
pytest (pip install pytest) ist der beliebteste Testrunner. Tests sind einfache Funktionen mit assert, und bei Fehlern zeigt pytest sehr genau, was abwich:
# datei: test_rechnen.py
def test_addition():
assert 1 + 1 == 2
def test_division_durch_null():
import pytest
with pytest.raises(ZeroDivisionError):
1 / 0Ausführen im Terminal:
pytest # alle Tests im Projekt
pytest -v # ausführliche Ausgabe
pytest -k primzahl # nur Tests, deren Name "primzahl" enthältParametrisierte Tests
Mit @pytest.mark.parametrize läuft derselbe Test mit vielen Eingaben:
import pytest
@pytest.mark.parametrize("n, erwartet", [(2, True), (4, False), (13, True)])
def test_prim(n, erwartet):
assert ist_primzahl(n) == erwartetFixtures
Fixtures liefern wiederverwendbare Testdaten oder Vorbereitungen:
import pytest
@pytest.fixture
def warenkorb():
return {"Apfel": 2, "Birne": 1}
def test_menge(warenkorb):
assert sum(warenkorb.values()) == 3Was testet man?
- Normale Fälle (das erwartete Verhalten)
- Grenzfälle: leere Liste, Null, negative Zahlen, sehr große Werte
- Fehlerfälle: Was passiert bei falscher Eingabe?
- Jeden Fehler, den du gefunden hast, als Test festhalten, damit er nicht zurückkommt
Tipp
Schreibe die Funktion so, dass sie ein Ergebnis zurückgibt statt nur etwas auszugeben. Dann kannst du sie leicht testen.
Merke
- Tests prüfen automatisch, ob Code das Erwartete tut
assertist der einfachste Test,unittestist eingebaut- pytest: Funktionen mit
assert,parametrizeund Fixtures - Teste normale Fälle, Grenzfälle und Fehlerfälle
- Jeder gefundene Fehler wird zum Test
Übungsaufgabe
Schreibe eine Funktion ist_palindrom(text) und mindestens fünf Tests dafür, darunter Groß-/Kleinschreibung und den leeren Text.
Quiz zur Selbstkontrolle
Was passiert, wenn ein assert nicht stimmt?
- Ein AssertionError wird ausgelöst (richtig)
- Das Programm läuft still weiter
- Der Wert wird korrigiert
- Python startet neu
Wozu dient parametrize in pytest?
- Denselben Test mit vielen Eingaben auszuführen (richtig)
- Tests zu überspringen
- Funktionen zu kopieren
- Fehlermeldungen zu verstecken
Was sind Grenzfälle?
- Extreme oder ungewöhnliche Eingaben wie leer, null oder negativ (richtig)
- Tests, die immer bestehen
- Sicherheitslücken
- Syntaxfehler
Weiter im Kurs
Zurück: Typ-Hints
Weiter: Kommandozeilen-Programme mit argparse
Alle Kapitel: Python im Überblick