programmierung
und datenbanken
Joern Ploennigs
Unit-Tests
Ablauf¶
Umgang mit Programmfehlern - Strategien¶
5 Hauptstrategien:
- Fehlervermeidung
- Fehlererkennung
- Fehlerbehebung
- Fehlerbehandlung
- Fehlerausschluss
Jede Strategie hat ihre eigenen Methoden und Werkzeuge
1. Fehlervermeidung¶
Grundprinzip: Beginnt mit Erfahrung darüber, wo Fehler entstehen können
Strategien für sicheren Code:
- Code Reviews → Jede Codezeile wird von einem anderen Programmierer geprüft
- Test-Driven Development → Man schreibt den Test vor dem eigentlichen Code
- Full Test Coverage → Jede Codezeile sollte mindestens einen Test haben
Achtung: Vorbeugung kann viel Vorarbeit bedeuten
2. Fehlererkennung¶
Verschiedene Fehlertypen erfordern verschiedene Werkzeuge
Syntaktische Fehler:
- Automatisch in der IDE erkannt
Statische Fehler:
- Mittels Lint-Tools (statische Code-Analyse)
- Regelbasierte Verfahren
- Moderne IDEs nutzen intern Lint-Tools
Dynamische Fehler:
- Prüfen durch automatisch ausgeführte Unit-Tests
- Nach jeder Code-Änderung
- Testen Funktionen/Methoden systematisch
2. Fehlererkennung – Unit-Tests¶
Unit-Tests sind zusätzlich geschriebener Code, der nur zum Testen eines Moduls (Funktion, Klassen, Module) geschrieben wurde.
- Wichtige Eigenschaften:
- Oft ist Test-Code umfangreicher als der zu testende Code
- Ziel: Prüfen ob Modul gewünschte Funktion korrekt ausführt
- White-Box-Test: Modul mit bestimmten Eingaben aufrufen
- Erwartete Ausgaben oder Exceptions prüfen
- Sollte automatisierte Ausgeführt werden nach jeder Code-Änderung
2. Fehlererkennung – Unit-Test Typen¶
Funktionstests
Korrekte Erfüllung der Funktion
Beispiel: Division-Modul
zaehler = 10
nenner = 2
ergebnis = division(zaehler, nenner)
assert ergebnis==5 # Erwartet: 5
Grenzwerttests
Korrekter Umgang mit Grenzwerten
zaehler = 10
nenner = 0
ergebnis = division(zaehler, nenner)
assert ergebnis==None # Erwartet: None
Datentyptests
Korrekter Umgang mit unerwarteten Eingaben
zaehler = 'keine_zahl'
nenner = 0
try:
ergebnis = division(zaehler, nenner)
assert False # We let the test fail
except TypeError as e:
# Erwartet: TypeError
3. Fehlerbehebung - Grundlagen¶
Debugging ist das systematische Suchen und Beheben von Fehlern.
Methoden:
- Logging-Nachrichten zur Verfolgung des Programmflusses
- Debugger für detaillierte Analyse
Debugger-Funktionen:
- Schrittweise Ausführung von Codezeilen
- Überwachen von Variablenwerten
- Dynamisches Einfügen von Code
3. Fehlerbehebung – In Python¶
- Das systematische Debugging ist eine Kernkompetenz jeden Programmierers
- Python Debugging-Tools
- Standard-Debugger: pdb (Python-Modul)
- IDE-eigene Debugger (oft benutzerfreundlicher)
- Mehr Details dazu in der Übung!
4. Fehlerbehandlung - Auf alles vorbereitet sein¶
Realität: Allen Bemühungen zum Trotz werden in realen Programmen Fehler zur Laufzeit auftreten
Ziel:
- Fehler abfangen und behandeln
- Programm läuft weiter oder beendet kontrolliert
- Mechanismus:
- Dynamischer Fehler → Exception wird erzeugt
- Python-Werkzeuge:
try
,except
,raise
4. Fehlerbehandlung - In Python¶
try:
Enthält ein (vermutlich fehleranfälliges) Stück Programmcodeexcept:
Tritt im try-Block ein bestimmter Fehler (Exception) auf, wird dieser beendet und der passende except-Block aufgerufen.raise:
Wird in einem except-Block aufgerufen um eine Exception weiterzugebenfinally:
Wird nach dem Ende des try-Blocks aufgerufen, egal ob es einen Fehler gab oder nichtelse:
optional definiert nach allen except-Blöcken, wird ausgeführt falls im try-Block keine Exception auftritt
Semantische Fehler - Exceptions¶
Exception-Mechanismus:
- Unterbrechen normalen Programmverlauf
- Kommunizieren Fehler mit Fehlermeldung
- Verhindern unkontrolliertes Abstürzen
- Werden im Stack nach oben weitergegeben
- Bis sie abgefangen oder Programm abstürzt
Ziel: Programm in lauffähigen Zustand halten
Semantische Fehler – Exceptions abfangen¶
Bekannte Exceptions sollten immer abgefangen werden
Python try-except
-Block:
try:
# Block mit erwarteter Exception
ergebnis = zaehler / nenner
except:
# Fehlerbehandlung
print("Division durch 0")
ergebnis = None
Semantische Fehler - Exception-Ebenen¶
Exceptions werden auf mehreren Ebenen genutzt:
- Betriebssystem - Vordefinierte System-Exceptions
- Programmiersprache - Eingebaute Language-Exceptions
- Eigener Code - Selbst definierte Exceptions
Einsatzgebiete (wo Fehler wahrscheinlich sind):
- Kommunikation mit externen Quellen
- Lesen von Dateien
- Nutzereingaben
- Netzwerkverbindungen
4. Fehlerbehandlung - Beispiel: Division durch 0¶
zaehler = 12
nenner = 0
try:
ergebnis = zaehler / nenner
except:
print("Exception")
Frage: Welche Exceptions können hier geworfen werden?
4. Fehlerbehandlung - Beispiel: Division durch 0¶
zaehler = 12
nenner = 0
try:
ergebnis = zaehler / nenner
except:
print("Exception")
Frage: Welche Exceptions können hier geworfen werden?
Mögliche Exceptions:
ZeroDivisionError
- Division durch 0TypeError
- Falscher DatentypNameError
- Variable nicht definiert
4. Fehlerbehandlung - Exceptions propagieren¶
- Wichtiges Konzept in komplexerer Software
- Wir können Fehler zwar lokal behandeln, wollen aber dennoch die "oberen Ebenen" des Programmes darüber informieren.
- Die
raise
-Anweisung "wirft" dafür aus einemexcept
-Block eine Exception weiter nach oben.
4. Fehlerbehandlung - Beispiel: Exception weiterreichen¶
def count_up_and_down(x):
if x <= 0:
raise ValueError("No negative values allowed")
# Verknüpft vorwärts- und rückwärts-Liste
return list(range(x)) + list(reversed(list(range(x-1))))
try:
print(count_up_and_down(-6))
except ValueError:
print("That value was invalid.")
5. Fehlerausschluss - Korrektheit von Programmen mathematisch beweisen¶
Gewisser Programmcode kann mittels mathematischen Beweises validiert werden
Hoare-Kalkül: Programm Zeile für Zeile mit mathematischer Logik formalisieren
Von vorne nach hinten durch das gesamte Programm
Problem: Für die meisten Anwendungen zu zeitaufwendig und komplex
Einsatz: Nur bei kritischen Systemen (Medizintechnik, Luftfahrt, etc.)
fragen?
und datenbanken