Exkurs - Modultest in Python
Korrektheitsüberprüfung mit Testfällen
Im letzten Abschnitt haben wir Beispiele für Funktionsaufrufe benutzt, um das Verhalten einer Funktion zu beschreiben.
def anzahlTageImMonat(monat, jahr):
"""
monat: natürliche Zahl aus dem Bereich 1..12
jahr: natürliche Jahr
return: natürliche Zahl aus dem Bereich 1..31
Beispiele:
>>> anzahlTageImMonat(2, 2012)
29
"""
...
Python stellt eine Möglichkeit zur Verfügung, wie man solche Beispiele für Funktionsaufrufe
- die im Documentation String einer Funktion aufgeführt sind -
als Testfälle automatisiert überprüfen kann.
Hierzu muss man nur die Funktion testmod
des Moduls doctest
ausführen:
def schaltjahr(jahr):
"""
jahr: int
Beispiele:
>>> schaltjahr(2012)
True
>>> schaltjahr(2100)
False
>>> schaltjahr(2000)
True
"""
if (jahr % 400 == 0) or ((jahr % 4 == 0) and not (jahr % 100 == 0)):
return True
else:
return False
def anzahlTageImMonat(monat, jahr):
"""
monat: natürliche Zahl aus dem Bereich 1..12
jahr: natürliche Jahr
return: natürliche Zahl aus dem Bereich 1..31
Beispiele:
>>> anzahlTageImMonat(2, 2012)
29
>>> anzahlTageImMonat(3, 2012)
31
"""
if monat in [1, 3, 5, 7, 8, 10, 12]:
anzahl = 31
elif monat in [2, 4, 6, 9, 11]:
anzahl = 30
elif schaltjahr(jahr):
anzahl = 29
else:
anzahl = 28
return anzahl
def naechstesDatum(datum):
"""
datum: Tupel mit 3 Zahlen zur Datumsbeschreibung
return: Tupel zur beschreibung des Folgedatums
Beispiele:
>>> naechstesDatum((21, 1, 2012))
(22, 1, 2012)
>>> naechstesDatum((31, 1, 2012))
(1, 2, 2012)
>>> naechstesDatum((31, 12, 2012))
(1, 1, 2013)
"""
(tag, monat, jahr) = datum
if tag < anzahlTageImMonat(monat, jahr):
tag = tag + 1
elif monat < 12:
tag = 1
monat = monat + 1
else:
tag = 1
monat = 1
jahr = jahr + 1
return (tag, monat, jahr)
from doctest import testmod
testmod()
Die Testfälle im Documentation String deuten wir hier als Funktionsaufrufe mit deren erwarteten Ergebnissen.
Wenn Python die Funktion testmod()
ausführt, dann werden automatisch alle Testfälle im
Documentation String überprüft.
Aufgabe 1
(a) Speichere den oben gezeigten Python-Quelltext in einer Datei tagezaehlen.py
ab
und führe das Programm aus. Beim Überprüfen der Testfälle stellt Python hier fest, dass etwas nicht stimmt.
Versuche anhand der Rückmeldungen von Python den Fehler zu finden.
(b) Korrigiere den Fehler und führe das Programm erneut aus. Wie zeigt sich, dass alle Testfälle erfolgreich überprüft wurden.
(c) Die Rückmeldungen von Python kann man auch etwas geschwätziger (eng. verbose) gestalten. Probiere das selbst aus - mit fehlerhaften und korrekten Testfällen.
def schaltjahr(jahr):
...
from doctest import testmod
testmod(verbose=True)
Aufgabe 2
Betrachte die folgende Situation, in der Bausteine zum Tagezählen in einer Anwendung benutzt werden.
Bausteine:
def schaltjahr(jahr):
...
from doctest import testmod
testmod()
Verwendung der Bausteine:
from tagezaehlen import *
# Initialisierung
datumStart = (15, 1, 2012)
datumZiel = (16, 4, 2015)
# Verarbeitung
datumAktuell = datumStart
anzahlTage = 0
while datumAktuell != datumZiel:
datumAktuell = naechstesDatum(datumAktuell)
anzahlTage = anzahlTage + 1
# Ausgabe
print("Startdatum:", datumStart)
print("Zieldatum:", datumZiel)
print("Anzahl der Tage:", anzahlTage)
Hier werden die Testfälle jedesmal überprüft, wenn der Quelltext zu den Bausteinen ausgeführt wird - also jedesmal, wenn das Anwendungsprogramm ausgeführt wird.
Das ist natürlich unnötig. Profis verwenden die Funktion testmod()
daher wie folgt:
Bausteine:
def schaltjahr(jahr):
...
if __name__ == "__main__":
from doctest import testmod
testmod()
Verwendung der Bausteine:
from tagezaehlen import *
# Initialisierung
datumStart = (15, 1, 2012)
datumZiel = (16, 4, 2015)
# Verarbeitung
datumAktuell = datumStart
anzahlTage = 0
while datumAktuell != datumZiel:
datumAktuell = naechstesDatum(datumAktuell)
anzahlTage = anzahlTage + 1
# Ausgabe
print("Startdatum:", datumStart)
print("Zieldatum:", datumZiel)
print("Anzahl der Tage:", anzahlTage)
Probiere das selbst aus. Überzeuge dich (z.B. mit der geschwätzigen testmod-Version), dass Python in der Profi-Version die Testfälle nicht bei jeder Anwendung überprüft sondern nur, wenn der Bausteinquelltext als Hauptprogramm direkt ausgeführt wird.