Abschlussprojekt: Eine gemeinschaftliche Leihbibliothek · Übung
Das System testen, umstrukturieren und überprüfen
Das System ist fertig. Jede Operation der Schnittstellenzusage funktioniert. Die verdeckten Prüfungen haben das bei jedem Schritt bestätigt.
Das ist nicht dasselbe, wie selbst zu wissen, dass es funktioniert.
Fremde Tests beweisen weniger, als du denkst
Jede bisherige Lektion hat deinen Code mit Prüfungen bewertet, die jemand anderes geschrieben hat. Das ist nützlich und auf bestimmte Weise begrenzt: Sie sagen dir, ob du eine Spezifikation erfüllt hast. Sie sagen dir nicht, ob du verstehst, was du gebaut hast.
Kapitel 13 begründete, warum du eigene Tests schreiben solltest. Hier tust du es für ein selbst entworfenes System. Der Unterschied: Niemand hat dir gesagt, welche Fälle wichtig sind. Sie auszuwählen ist die Arbeit.
Wo du hinsiehst
Eine Schnittstellenzusage dieser Größe hat mehr prüfbare Aussagen, als irgendjemand testen würde. Drei Fragen grenzen sie ein.
Was geht unbemerkt kaputt? Die Ausleihgrenze wird durch Zählen geprüft. Wäre die Anzahl falsch, würde nichts abstürzen: Ein Mitglied dürfte einfach dauerhaft einen Gegenstand zu viel ausleihen, und niemand würde es bemerken.
Was hat eine Grenze? Überfällig bedeutet >, nicht >=. Lektion 5 aus Kapitel 13 erklärte, dass ein Fall auf jeder Seite einer Grenze mehr wert ist als zehn Fälle in der Mitte.
Was hast du beim Schreiben falsch gemacht? Das ist die ehrlichste Quelle für einen Test. Wenn du für etwas zwei Versuche brauchtest, gehört der Test, der es erkannt hätte, in die Datei.
Mindestens ein Fehlerpfad
Die Schnittstellenzusage verlangt das ausdrücklich. Es ist die Hälfte, die oft ausgelassen wird.
Zu testen, dass eine Ausleihe funktioniert, ist einfach und beruhigend. Zu testen, dass eine abgelehnte Ausleihe aus dem richtigen Grund abgelehnt wird und nichts zurücklässt, zeigt dir, ob die vorzeitigen Prüfungen aus Lektion 5 in der richtigen Reihenfolge stehen:
from pythonland_test import raises
def test_a_second_borrower_is_refused():
shelf = build_library()
shelf.checkout("b1", "m1", 10)
with raises(ItemUnavailableError) as caught:
shelf.checkout("b1", "m2", 11)
assert caught.value.item_id == "b1"
Lektion 8 gab diesem Fehler seine Attribute, damit ein Test etwas Genaues sagen kann. Ein Test, der nur „Irgendein Fehler ist aufgetreten“ prüft, würde bei allen vier Ablehnungen bestehen. Anders gesagt: Er prüft fast nichts.
Warum eigene Tests schreiben, wenn die verdeckten Prüfungen bereits bestehen?
Mit den Tests im Rücken umstrukturieren
Sobald die Tests grün sind, betrachte deine library.py mit der Freiheit aus Kapitel 13: Die interne Implementierung darf sich ändern, solange die öffentliche Schnittstellenzusage gleich bleibt. Tests liefern Hinweise, können aber Verhalten übersehen, das die Zusage weiterhin verlangt.
Typische Kandidaten, wobei die Antwort durchaus „nichts“ sein darf:
Eine Methode mit mehreren unabhängigen Entscheidungen kann verständlicher werden, wenn eine davon in eine gezielte Hilfsmethode ausgelagert wird. Leerzeilen allein sind kein Grund zum Aufteilen.
Ein Kommentar, der erklärt, was ein Block tut, ist oft ein noch ungenutzter Methodenname.
Wiederholtes Nachschlagen wie
self._items[item_id]in mehreren Methoden könnte eine kleine private Hilfsmethode rechtfertigen.
Wenn du etwas änderst, führe die Tests nach jedem Schritt aus statt erst am Ende. Das ist die gesamte Disziplin und der Grund, warum die Tests zuerst kamen.
Was du jetzt baust
tests/test_library.py ist die Datei im Editor. Schreibe mindestens fünf Tests: den gewöhnlichen Ablauf, eine Ablehnung mit Prüfung von Typ und Attributen, eine Überfälligkeitsgrenze und alles andere, von dem du in sechs Monaten wissen möchtest, ob es noch funktioniert.
Verwende während der Arbeit Run tests. Gib ab, wenn sie bestehen.
Aufgabe
Schreibe deine eigenen Tests für das System in tests/test_library.py.
Mindestens fünf Testfunktionen, die mindestens Folgendes abdecken:
Ein Mitglied leiht einen Gegenstand aus, und dieser verschwindet aus
available_items().Einer zweiten Person wird derselbe bereits ausgeliehene Gegenstand mit
ItemUnavailableErrorverweigert. Prüfe sowohl den Typ als auch das Attributitem_idoderdue_day.Die Überfälligkeitsgrenze am Fälligkeitstag und am Tag danach.
Eine Rückgabe und die erneute Verfügbarkeit des Gegenstands.
Importiere raises aus pythonland_test für die Ablehnung. Jeder Test bereitet seine eigene Bibliothek vor, damit kein Test davon abhängt, dass ein anderer bereits ausgeführt wurde.
Lies dann library.py erneut und strukturiere alles um, was du übersichtlicher haben möchtest. Führe nach jeder Änderung Tests aus. Wenn nichts geändert werden muss, sag das und lass es unverändert: Ein Refactoring, das niemand braucht, ist keine Verbesserung.