0%

Objektorientierte Programme testen und umstrukturieren · Übung

Fehlschläge lesen und Kopplung erkennen

Run tests meldet vier unterschiedliche Ergebnisse. Sie auseinanderzuhalten spart Zeit.

Passed (Bestanden). Nichts zu tun.

Failed (Fehlgeschlagen). Eine Zusicherung war falsch. Das Verhalten entspricht nicht dem, was der Test verlangt. Der Bericht nennt den Test, die Datei, die Zeile und deine Meldung. Das ist die hilfreiche Art: Die Maschine sagt dir etwas Konkretes über dein Programm.

Error (Fehler). Der Test hat etwas ausgelöst, das keine Zusicherung ist: einen TypeError, einen AttributeError, einen nicht vorhandenen Namen. Der Test begann, konnte aber nicht normal enden. Die Ursache kann in seiner Vorbereitung oder in der Anwendung liegen: Ein Bericht, der bei einem leeren Quiz durch null teilt, ist ein Anwendungsfehler. Lies den Traceback und finde die auslösende Operation, bevor du entscheidest, was zu korrigieren ist.

Collection error (Fehler beim Sammeln). Eine ganze Datei konnte nicht geladen werden: wegen eines Syntaxfehlers oder eines Imports von etwas, das nicht existiert. Nichts in dieser Datei wurde ausgeführt. Behebe das zuerst, denn bis dahin bestehen die Tests dieser Datei weder, noch schlagen sie fehl.

Daraus ergibt sich die Reihenfolge: zuerst Fehler beim Sammeln, dann Fehler, dann fehlgeschlagene Zusicherungen. Ein einzelner fehlender Import kann eine ganze Seite alarmierender Ausgabe verursachen, die mit seiner Korrektur auf einmal verschwindet.

Die Meldung vor dem Traceback lesen

Ein Fehlschlag sieht ungefähr so aus:

test_a_four_out_of_five_attempt_is_a_b
  test_grades.py, line 14
  AssertionError: expected Grade: B, got Grade: C

Die letzte Zeile sagt dir, was passiert ist. Die Zeilennummer sagt, wo du nachsehen musst. Der Traceback darüber, falls vorhanden, zeigt, wie die Ausführung dorthin gelangte. Er ist vor allem dann nützlich, wenn das Problem ein Fehler statt einer fehlgeschlagenen Zusicherung ist.

Deshalb bestand Lektion 5 auf einer Meldung in einem Test mit Schleife. AssertionError allein schickt dich zurück zum Testcode. Bei expected Grade: B, got Grade: C musst du das oft gar nicht mehr.

Wenn ein Test schwer zu schreiben ist, sagt dir der Code etwas

Hier beginnt die zweite Hälfte der Lektion. Es geht um eine Idee zum Programmentwurf, nicht zum Testen.

Um zu prüfen, dass ein Versuch mit 4 von 5 Punkten ein B erhält, musst du ein Quiz bauen, Fragen mit insgesamt 5 Punkten erfinden, von denen sich genau 4 erzielen lassen, sie hinzufügen, einen Versuch erstellen, einige richtig und andere falsch beantworten, dann den Bericht aufrufen und die zweite Zeile ansehen.

Sechs oder sieben Zeilen Vorbereitung für einen String. Der Großteil hat mit Noten gar nichts zu tun. Es geht darum, die beiden Zahlen 4 und 5 durch eine Kette von Objekten an die richtige Stelle zu bringen.

Vergleiche damit, wovon die Notenzeile tatsächlich abhängt: einem Punktestand und einer Gesamtpunktzahl. Zwei Zahlen. Alles andere in der Vorbereitung existiert, weil die Notenzeile nur über einen vollständig aufgebauten Attempt erreichbar ist.

Dieser Abstand hat einen Namen. Die Notenzeile ist an Quiz, Question und Attempt gekoppelt, obwohl die Idee, die sie ausdrückt, keines dieser Objekte braucht.

Ein Test benötigt acht Zeilen Vorbereitung, um einen kurzen String zu prüfen. Was ist die hilfreichste Schlussfolgerung?

Mühe ist ein Hinweis, kein Beweis

Gehe vorsichtig mit dieser Idee um, denn sie lässt sich leicht übertreiben.

Manche Vorbereitung ist berechtigt. Lektion 4 bereitete ein Quiz und einen Versuch vor, um report_lines zu testen. Das war richtig: Beim getesteten Verhalten ging es tatsächlich um die Zusammenarbeit dieser Objekte.

Das Signal lautet nicht „Dieser Test hat Vorbereitung“, sondern Vorbereitung, die nichts mit dem Geprüften zu tun hat. Fragen und Antworten sind kein Teil der Idee „80 % ist ein B“. Wenn sie trotzdem in der Vorbereitung auftauchen, erfüllen sie die Struktur des Codes statt die Bedeutung des Verhaltens.

Bemerke es, schreibe den Test trotzdem und lass ihn das Problem dokumentieren. Lektion 9 unternimmt etwas dagegen.

Aufgabe

Schreibe test_grades.py und prüfe die Notenzeile in zwei Notenbereichen, die das Quiz dieses Kapitels nicht erreichen kann.

Importiere das Benötigte und schreibe dann drei Tests:

  • test_a_four_out_of_five_attempt_is_a_b: Baue ein Quiz mit 5 möglichen Punkten, bei dem sich genau 4 erzielen lassen. Beantworte entsprechend und prüfe, dass report_lines(attempt)[1] gleich "Grade: B" ist.

  • test_a_seven_out_of_ten_attempt_is_a_c: Dasselbe für 7 von 10 Punkten und "Grade: C".

  • test_the_letter_alone_needs_no_quiz: Prüfe, dass grade_letter(0.8) gleich "B" und grade_letter(0.7) gleich "C" ist, ohne irgendwo im Test ein Quiz, eine Frage oder einen Versuch zu verwenden.

Schreibe die ersten beiden auf dem direkten Weg über den Bericht. Achte darauf, wie viel Vorbereitung dem Erreichen der Notenzeile dient statt den Noten selbst. Der dritte Test trifft dieselbe Aussage ohne all das und fällt bewusst aus der Reihe: Er kostet fast keine Arbeit und beweist nichts über den Bericht.