Kapitel 4. Testgetriebene Entwicklung mit PHPUnit

"The fewer tests you write, the less productive you are and the less stable your code becomes."

--Erich Gamma

Einleitung

Das Testen von Software ist wichtig. Doch obwohl dies allen Softwareentwicklern bewusst sein dürfte, verhalten sich die meisten nicht entsprechend. Sie testen ihre Software entweder gar nicht, oder erst, wenn es zu spät ist. Johannes Link führt die folgenden Vorurteile an, die zu einem Teufelskreis führen, dem nur schwer zu entkommen ist [Link2005]:

  • "Ich habe keine Zeit zum Testen."

  • "Testen von Software ist langweilig und stupide."

  • "Mein Code ist praktisch fehlerfrei, auf jeden Fall gut genug."

  • "Die Testabteilung testet. Die können das eh viel besser."

Im Extreme Programming, das zu den so genannten agilen oder leichtgewichtigen Software-Entwicklungsprozessen gehört, wird dieser Teufelskreis durchbrochen, und zwar durch die Forderung, den Test zuerst zu schreiben und danach den Code, auf den sich der Test bezieht (Test-First-Ansatz). Das Vertrauen in den Code wird erhöht, die Auswirkungen von Änderungen an einer Stelle auf den restlichen Code können schnell und zuverlässig überprüft werden. Darüber hinaus führt diese Vorgehensweise zu Einfachheit, Testbarkeit und Wartbarkeit des Codes.

Das Schreiben von neuem Produktionscode gestaltet sich beim Test-First-Ansatz in zwei Schritten:

  1. Bevor neuer Produktionscode geschrieben wird, wird ein entsprechender Test geschrieben, der diesen Code motiviert.

    Dies sorgt einerseits dafür, dass es zu keinem Zeitpunkt Produktionscode gibt, für den kein Test existiert. Andererseits setzt sich der Programmierer beim Formulieren des Tests mit den Anforderungen an den zu schreibenden Code auseinander.

  2. Es wird nur so viel Produktionscode geschrieben, wie es der Test verlangt. Mit anderen Worten: Läuft der Test, steht der Code.

Ohne die automatisierte Ausführung von Entwicklertests auf Modulebene (englisch: Unit Tests), wie wir sie in diesem Kapitel diskutieren wollen, ist das Refactoring (deutsch: "neu herstellen") von Code nur schwer möglich. Martin Fowler definiert Refactoring als "den Prozess, ein Softwaresystem so zu ändern, dass sich das externe Verhalten nicht ändert, jedoch die innere Struktur verbessert wird". [Fowler1999] Hierzu gehören unter anderem Änderungen an der Struktur des Codes wie Umbenennung von Klassen und Methoden oder die Extraktion von Code einer Klasse in eine neue Klasse.

Der Begriff des Unit Tests kommt ursprünglich aus der prozeduralen Programmierung. Dort entsprach die zu testende Programmeinheit einer Funktion oder Prozedur. In der objektorientierten Programmierung ist diese Definition weiter gefasst, so dass von einer einzelnen Methode, über eine gesamte Klasse bis hin zum gesamten System Programmeinheiten mit einem Unit Test getestet werden können.

Unit Tests ermöglichen das ständige Refactoring von Code auf der Basis der folgenden Regeln:

  1. Alle Unit Tests laufen.

  2. Der Code kommuniziert alle seine Designkonzepte.

  3. Der Code enthält keine Redundanz.

  4. Der Code enthält, unter Berücksichtigung der obigen Regeln, die geringstmögliche Anzahl an Klassen und Methoden.

Erst automatisierte Tests machen die zum Erreichen des einfachsten Designs nötigen Änderungen am Code sinnvoll durchführbar. Ohne sie müsste jede Methode einer jeden Klasse von Hand getestet werden, wenn der Code einer Klasse geändert wurde.

In der Java-Welt bildet JUnit den De-facto-Standard unter den Frameworks für Unit Tests. JUnit definiert eine allgemeine Struktur für Testfälle, gibt dem Entwickler die nötigen Werkzeuge an die Hand, um diese auszuführen, und übernimmt so einen großen Teil der Arbeit. Unter dem Namen PHPUnit habe ich eine PHP-Portierung von JUnit entwickelt, die über PEAR als Open-Source-Software bezogen werden kann.

Test-Frameworks wie PHPUnit und JUnit erlauben die strikte Trennung von Produktionscode und Testcode. Tests können gruppiert werden, die Ausführungsreihenfolge hat keinen Einfluss auf das Ergebnis der einzelnen Tests.

Neben PHPUnit2, der aktuellen Version von PHPUnit für PHP 5, gibt es mit PHPUnit eine Version für PHP 4, die nicht so umfangreich wie die Implementierung für PHP 5 ist und nicht mehr weiterentwickelt wird. PHPUnit2 enthält das auf JUnit basierte Unit-Test-Framework sowie ein Frontend für die Verwendung von der Kommandozeile. Neben der Funktionalität von JUnit bietet PHPUnit2 unter anderem auch die Funktionen von junitour für unvollständige Tests und TestDox für die agile Dokumentation.

Die Installation des PHPUnit2-Paketes gestaltet sich dank des PEAR-Installers recht komfortabel:

Abbildung 4.1. Das PHPUnit2-Paket installieren

pear install --alldeps PHPUnit2


Im Folgenden betrachten wir die von PHPUnit2 bereitgestellten Klassen und werden sehen, wie mit ihnen Unit Tests geschrieben und automatisiert ausgeführt werden können.