Article Banner

Unit Tests und Code Coverage in PHP

Beitrag vom 20.07.2016

Meine Lambda Library für PHP hat mittlerweile einen Status erreicht, den man als "semi-stabil" bezeichnen könnte.

In den letzten Wochen sind weniger neue Features dazu gekommen. Eher habe ich mich mit Unit Tests und Code Coverage beschäftigt. Im Readme befinden sich auch entsprechende Bildchen von Travis CI und Coveralls.

Build Status Coverage Status

Unit Tests

In Unit Tests werden für Code Prüfungen definiert, die feststellen sollen, dass der Code korrekt arbeitet. Ansich ist das nichts anderes als:

Führe Funktion X aus und erwarte als Ergebnis "ABC"

Speziell bei komplexen Libraries kann dies aber schnell ausarten. Zum jetzigen Zeitpunkt werden in meinem Fall in 23 verschiedenen Tests insgesamt 176 Prüfungen durchgeführt.

Ein simples Beispiel:

<?php
class LambdaTest extends PHPUnit_Framework_TestCase
{
    public function testScalar()
    {
        $s = new \PerrysLambda\ScalarProperty("Zähn € zahme Ziegen zögen zwei Zentner Zücker zum Zoö!", 'UTF-8');
        $this->assertSame(true, $s->startsWith("Zä"));
    }
}

In PHP ist ein Unit Test nichts anderes als eine Klasse mit vielen öffentlichen Methoden. Die Klasse erbt dabei von der Test Case Klasse im PHPUnit Framework und hat somit einen haufen von Prüfmethoden zur Verfügung.

Das Beispiel hier macht nichts anderes als:

Die Methode startsWith muss als Wert true zurück geben

Schlägt diese Prüfung fehl, wird dies in dem Ergebnis Protokoll von PHPUnit vermerkt. So kann für jede vorhandene Methode ein Test verfasst werden. Das geht soweit, dass auch geprüft werden kann, ob eine Methode korrekt Exceptions wirft.

<?php
/**
 * @expectedException \PerrysLambda\IO\IOException
 */
public function testAntiDirectory()

Die meisten IDEs bieten entsprechend Integrationsmöglichkeiten für PHPUnit. So muss man in Netbeans zum Beispiel nur den Pfad zu PHPUnit angeben. Anschließend kann man die Tests einfach lokal in Netbeans ausführen.

Im Hauptverzeichnis des Code Projekts müssen sich jeweils eine phpunit.xml.dist Datei und ein test Ordner befinden. In der XML Datei wird definiert wie sich die Tests aufbauen und wie diese ausgeführt werden sollen.

Im test Ordner finden sich die eigentlichen Tests.

Ist alles korrekt muss nur im Ordner in der sich die XML Datei befindet der phpunit Befehl ausgeführt werden.

Travis CI

Mit Travis CI kann das Unit Testing mit Github verknüpft werden. Jedes mal, wenn man den Code mit einem git push ins Github Repo sendet, startet Travis automatisch Unit Tests für verschiedene PHP Versionen.

Sehr praktisch, da man normalerweise immer nur eine PHP Version auf seiner Workstation installiert hat. In Github integriert sich Travis auf wWunsch soweit, dass automatisch Pull Requests mit getestet werden.

Die Einstellungen für Travis finden sich in der Datei .travis.yml.

Code Coverage

Mit Code Coverage kann geprüft werden, wie viel Prozent des Projekt Codes durch Unit Tests überprüft werden. Es wird ein hübscher, bunter Report erzeugt der genau aufzeigt, wo man einen Test vergessen hat, und wo noch Verbesserungspotential ist.

Coveralls

Ähnlich wie Travis ist coveralls.io ein Dienst welcher sich in Github und die Unit Tests einklinkt und einen bunten Bericht erzeugt.

In den Berichten für jede einzelne Code Datei wird bunt markiert, was geprüft wurde und was nicht.

Dabei wertet Coveralls ein Log aus, welches von PHPUnit bzw Travis erzeugt wurde. Eine entsprechende Einstellung findet sich in der phpunit.xml.dist Datei.

In der .travis.yml Datei wird dabei ein Trigger definiert, der das Log an Coveralls weiter gibt. Es muss dabei mit dem composer Paketmanager gearbeitet werden, der die entsprechenden Abhängigkeiten automatisch während des Builds nachinstalliert.

Sollte an sich kein Problem sein, da man meistens eh schon eine composer.json Datei im Projekt hat.

Eine lokale Version habe ich davon nicht laufen. Vielleicht später.

Fazit

Hat man das ganze Konstrukt einmal richtig aufgesetzt, ist das eine riesige Hilfe. Ich habe in den letzten Wochen noch haufenweise Bugs gefunden und behoben.

Auch haben die Tests oft genug verhindert, dass ich bei Änderungen wieder neue Bugs in den Code einbaue.

Man muss nur kontinuierlich die Berichte von Coveralls prüfen und neuen / geänderten Code in die Tests aufnehmen. Ein Restrisiko, dass man ein Szenario vergessen hat gibt es zwar noch, aber dies ist viel kleiner als ganz ohne die Tests.

Happy Testing.

Hallo Internet

Mein Name ist Christian, vom Beruf bin ich Anwendungsentwickler.

In meiner Freizeit beschäftige ich mich mit verschiedensten Technologien. Hier sammele ich Dinge, die für mich interessant waren oder sind.