Zum Inhalt springen

Aufgabe 20 - pytest II - Der Vertragstest

Zu Zen-Modus wechseln

Testen unter Vererbung und Polymorphismus, eine ausdrückliche Lehrplan-Kompetenz: Sie schreiben Vertragstests, die jede Subklasse einer Hierarchie bestehen muss, und weisen deren Wert nach, indem Sie absichtlich eingebaute Fehler in fremdem Code aufspüren (siehe Kapitel Testen mit pytest, Abschnitt Vertragstest). Getestet werden Ihre Formen-Hierarchie aus Aufgabe 12 und die Mediathek aus Aufgabe 09.

  • Kapitel Testen mit pytest vollständig; Aufgabe 19.
  • Ihre shapes.py (Aufgabe 12) und media.py (Aufgabe 09).
  • Für Teil C eine Tauschpartnerin oder einen Tauschpartner.
  • Sie definieren Verträge als Testklassen und lassen sie über Vererbung für jede Subklasse laufen.
  • Sie prüfen Gleitkommaergebnisse korrekt mit pytest.approx.
  • Sie beurteilen die Abdeckung einer Testreihe anhand real gefundener und verpasster Fehler.
  • Reproduktion: bekannte Testmuster auf einzelne Klassen anwenden (Teil A).
  • Reorganisation und Transfer: das Vertragstest-Muster auf eine ganze Hierarchie übertragen (Teil B).
  • Reflexion, Problemlösung und Urteilsbildung: Testlücken anhand eingeschleuster Fehler aufdecken, die Testreihe verbessern und ihren Wert für künftige Erweiterungen beurteilen (Teile C und D).

Die Übung ist auf etwa zwei Stunden ausgelegt. Teil D ist der Expertenteil.

  1. Schreiben Sie in test_shapes.py je einen Test pro Klasse mit bekannten Werten: Kreis mit Radius 1 hat Fläche pytest.approx(3.14159, abs=0.001); Rechteck 3 mal 4 hat Fläche 12 und Umfang 14; wählen Sie für das Dreieck ein Tripel mit bekanntem Ergebnis (etwa 3, 4, 5).
  2. Recherchieren Sie kurz, warum assert circle.area() == 3.14159 scheitern muss und was pytest.approx stattdessen tut. Zwei Kommentarsätze genügen.
  1. Definieren Sie eine Klasse ShapeContract (Name beginnt bewusst nicht mit Test) mit einer Methode create_shape(), die NotImplementedError wirft, und Vertragstests, die für jede Form gelten: area() und perimeter() liefern positive Zahlen; describe() liefert einen nicht leeren String, der den Klassennamen enthält.
  2. Leiten Sie pro konkreter Form eine Testklasse ab (TestCircle, TestRectangle, TestTriangle, dazu Ihr Sechseck oder Quadrat aus Aufgabe 12), die nur create_shape() überschreibt.
  3. Ergänzen Sie im Vertrag einen parametrisierbaren Fehlerfalltest: Ungültige Konstruktorargumente werfen ValueError. Da die Argumente je Klasse verschieden sind, lösen Sie das über eine weitere überschreibbare Methode invalid_arguments(), die eine Liste von Argument-Tupeln liefert.
  4. Führen Sie pytest -v aus und zählen Sie nach: Anzahl Vertragstests mal Anzahl Formklassen; stimmt die Testanzahl?
  1. Bauen Sie in eine Kopie Ihrer shapes.py genau einen subtilen Fehler in eine Subklasse ein (etwa Umfang des Rechtecks mit + statt * 2, oder Heron mit falschem Vorzeichen). Ihre Tauschpartnerin oder Ihr Tauschpartner tut dasselbe.
  2. Tauschen Sie die präparierten Dateien und lassen Sie Ihre eigene Testreihe gegen die fremde Datei laufen. Findet sie den Fehler?
  3. Falls nein: Welcher Test fehlte? Ergänzen Sie ihn, weisen Sie den Fehler damit nach und dokumentieren Sie den Fall (eingeschleuster Fehler, verpasst von, gefunden durch).

Teil D - Expertenteil: Vertragstest für die Mediathek

Abschnitt betitelt „Teil D - Expertenteil: Vertragstest für die Mediathek“
  1. Übertragen Sie das Muster selbstständig auf die Medium-Hierarchie: ein MediumContract mit mindestens vier Vertragstests (frisch erzeugt ist nichts entlehnt; nach borrow() schon; doppeltes borrow() wird abgelehnt; describe() enthält Titel und Jahr) und je einer Testklasse pro Medienart.
  2. Ergänzen Sie eine neue Medienart EBook(Medium) mit einem Zusatzfeld und binden Sie sie mit einer vierzeiligen Testklasse an. Wie viele Vertragstests laufen jetzt automatisch für die neue Klasse mit?
  3. Begründen Sie in drei Sätzen, warum genau dieser Mechanismus wertvoll ist, wenn im nächsten Jahr eine fremde Person die Hierarchie erweitert (Stichworte: Substitutionsprinzip, Regression).
  4. Grenzfrage: Nennen Sie eine Eigenschaft einer konkreten Medienart, die nicht in den Vertragstest gehört, und erklären Sie, woran Sie die Trennlinie zwischen Vertrag und Spezialfall festmachen.
  1. Warum darf die Vertragsklasse nicht mit Test beginnen, und was würde sonst passieren?
  2. Über welchen Mechanismus kommen die geerbten Tests zu ihrem jeweils passenden Testobjekt?
  3. Wozu dient pytest.approx, und warum ist == bei Gleitkommazahlen gefährlich?
  4. Ein eingeschleuster Fehler wurde von der Testreihe verpasst. Was sagt das über die Testreihe, und was ist die richtige Konsequenz?
  5. Was kostet die Anbindung einer neuen Subklasse an einen bestehenden Vertragstest, und was liefert sie?

test_shapes.py und test_media.py mit allen Vertragstests, das Fehlertausch-Protokoll aus Teil C und ein grüner pytest -v-Lauf. Alles committet.