PyDay: Unittests

Letzte Änderung am 11. Januar 2020 by Christoph Jüngling

In dem Artikel Unit-Tests habe ich vor längerem bereits etwas Grundlegendes dazu geschrieben. Auch das Thema Test-Driven Development wurde dort bereits erwähnt. Damals ging es speziell um ein Unittest-Framework für Microsoft Access, aber das generelle Konzept der Unittests ist natürlich nicht darauf beschränkt.

Wie man sich denken kann, steht auch für die hier besprochene Python-Entwicklungsumgebung ein Unit-Test-Framework zur Verfügung. Es gibt sogar mehrere davon, aber für unsere Zwecke reicht es zunächst, sich nur mit einem zu beschäftigen, PyUnit.

Warum Unit-Tests schreiben?

Manche sagen, “Testen? Da klicke ich mich einfach durch …”. Andere meinen “mach einfach keine Fehler, dann brauchst du auch nicht zu testen”. Gründe dagegen lassen sich sicher viele finden, aber es gibt auch ein paar Gründe dafür:

  • Ein Unit-Test-Framework ermöglicht es mir, die Tests mit wenig Aufwand (evtl. nur ein Ping Klick) auszuführen und das Ergebnis zu betrachten.
  • Unit-Tests stellen sicher, dass Programmteile immer wieder auf die gleiche Weise getestet werden.
  • Ich werde nach einem Test-Run sofort auf alle Probleme aufmerksam gemacht.
  • Ich kann nach einem Code-Refactoring durch meine Tests sicherstellen, dass der neue Code noch genauso funktioniert wie der alte (oder eben nicht, wodurch ich frühzeitig von Problemen erfahre).

Wie schreibt man einen Test?

Die Frage ist entscheidend mit einer anderen Frage verbunden: Was erwarte ich von meinen Funktionen? Wenn ich das nämlich klar definieren kann, dann habe ich nicht nur einen ersten Schritt in Richtung des Testdesigns gemacht, sondern genau genommen auch den ersten Schritt zu einer sinnvollen Programmierung.

Zunächst ein wenig Vorbereitung, dann kommen wir zu unseren ersten Tests.

Einrichtung in Eclipse/PyDev

Ordnerstruktur des Projektes "Kaffeemaschine" mit Unit-Test-Ordner

Ordnerstruktur

Die Tests sollten alle in einem Verzeichnis zusammengefasst stehen. So sind sie leicht gemeinsam ausführbar und müssen bei der späteren Auslieferung der Software nicht mit in das Setup eingepackt werden.

Aus diesem Grunde lege ich mir in meinem Projekt ein zweites Source-Verzeichnis an. Durch einen Rechtsklick auf das Projektverzeichnis im PyDev Package Explorer und dann “New -> Source Folder” ist das ganz einfach. Das Ergebnis sieht dann so aus wie im nebenstehenden Screenshot.

In den Ordner “tests” kommen nun alle Unit-Test-Klassen, die wir für dieses Projekt schreiben.

Mein erster Unittest

Wie schon gesagt, ich muss mir darüber klar werden, was ich eigentlich von einer Funktion oder Klasse erwarte. Wenn ich das zu Anfang schon weiß, dann kann ich genauso gut mit der Programmierung des Tests anfangen, bevor die Funktion oder Klasse existiert. Gar nicht so dumm, dachten sich auch viele Programmierer vor mir, sie nennen es “test driven development”.

Wir wollen eine Kaffeemaschine programmieren, und deren zentrales Element dann vermutlich eine Klasse sein sollte, die Kaffee kochen kann. Diese muss wissen, wieviel und welche Sorte, und sie meldet einen Status “fertig” zurück und gibt Informationen über Menge und Sorte Kaffee, die bereit steht. Damit haben wir bereits ein paar Schnittstellen:

  • sorte
  • menge
  • fertig

Die ersten beiden gehen in die Klasse rein, auf den dritten Parameter wird nur lesend zugegriffen. Mit diesen Informationen und der Festlegung, dass die Klasse “Kocher” heißen und im Modul “kocher” stehen soll (Python ist case-sensitiv!), können wir bereits unseren ersten Test schreiben. Zunächst fügen wir eine Testklasse hinzu (Rechtsklick auf den Source-Folder “tests”, dann “New” und einen Namen vergeben.  Den Test selbst schreiben wir zunächst einfach mal so hin (diesmal als Screenshot, um die von Eclipse sofort erkannten Fehler aufzuzeigen):

unittest-failed

Die Testklasse darf einen beliebigen gültigen Namen tragen, die Testmethode “testKocher” muss jedoch mit dem Wort “test” beginnen, da sie sonst vom Framework nicht ausgeführt wird. Doch soweit sind wir noch gar nicht. Zunächst zeigt der Editor bereits Fehler auf. Es ist natürlich klar, wieso das noch nicht funktionieren kann: Wir haben ja noch gar keine Klasse “Kocher”, die wir importieren könnten. Die fügen wir aber sofort hinzu (Rechtsklick auf “src”, dann “New -> PyDev Module”, Name “kocher”, dann das Template “Module: Class” auswählen).

class Kocher(object):

    def __init__(self, sorte, menge):
        self._sorte = sorte
        self._menge = menge
        self._fertig = False

Wenn wir den Test nun ausführen lassen, wird er “grün”. Das geht ganz einfach: Klicke mit der rechten Maustaste auf den Source-Folder “tests” und dann im Kontextmenü auf “Run as -> Python unit-test”. Evtl. muss noch die View “PyUnit” hinzugefügt werden.

Unittests automatisch ausführen

Unittests bei jedem Speichern ausführen

Unittests bei jedem Speichern ausführen

Wenn wir uns erst einmal an Unittests gewöhnt haben, werden wir sie nicht mehr missen wollen. Ein wesentlicher Teil der Arbeit damit ist allerdings, dass die Tests auch ausgeführt werden. Das können wir uns erheblich vereinfachen. In der View “PyUnit” finden wir unter den Symbolen das nebenstehend durch meinen Mauszeiger hervorgehobene. Der schwarze Kasten erläutert die Bedeutung dieses Icons. Sobald dieses eingedrückt ist, wird der zuletzt ausgeführte Testrun bei künftigen Speichervorgängen (die z.B. durch Strg+s ausgeführt werden können) automatisch wiederholt. Wir sehen also jederzeit an dem grünen oder roten Balken, ob unsere Tests erfolgreich waren oder nicht. Bequem, oder?

Das Ergebnis findest du in Changeset aea5a04.

Ähnliche Artikel:

Schreibe einen Kommentar

Deine Email-Adresse wird nicht veröffentlicht.

9 + dreizehn =