Artikelformat

Assertions mal anders

Unittests sollten wir ja alle schreiben und da kommen wir immer an den Punkt, an dem wir auch die Assertions definieren müssen. PHPUnit bietet hierfür einige Methoden an und – zumindest bei mir – findet man meistens assertEquals, assertSame und assertTrue/assertFalse. Aber man kann das ganze auch viel schöner verpacken und dafür gibt es natürlich eine Bibliothek.

Die geheimnisvolle Bibliothek nennt sich Hamcrest und ist in einige Sprachen portiert worden. Eine davon ist natürlich PHP und somit wird das ganze richtig interessant.

Bevor man alle seine Unittests anpasst muss man Hamcrest erst installieren. Es gibt ein pear-Repository und somit ist die Installation wie üblich möglich:

pear channel-discover hamcrest.googlecode.com/svn/pear
pear install Hamcrest/hamcrest

Um den Unterschied zu den PHPUnit-asserts zu verdeutlichen gibt es erstmal 2 Beispielklassen. Hierfür habe ich mir eine Klasse mit Metadaten für ein Dokument überlegt. Natürlich sind diese nicht vollständig, aber für ein Beispiel mehr als ausreichend. Es gibt nun also die beiden Klassen DocumentMetaData und DocumentOwner.

Der DocumentOwner sieht folgendermaßen aus:

class DocumentOwner {

        private /* String */ $firstName;
        private /* String */ $lastName;

        public function __construct($firstName, $lastName) {
                $this->firstName = $firstName;
                $this->lastName = $lastName;
        }

        public function getName() {
                return $this->lastName . ", " . $this->firstName;
        }

}

Die Metadaten werden so vorgehalten:

class DocumentMetaData {

        private /* int     */ $count;
        private /* Array   */ $author;
        private /* String  */ $language;
        private /* boolean */ $public;
        private /* DocumentOwner */ $owner;

        public function __construct($count, Array $author, $language, $public, DocumentOwner $owner) {
                $this->count = $count;
                $this->author = $author;
                $this->language = $language;
                $this->public = $public;
                $this->owner = $owner;
        }

        public function getCount() {
                return $this->count;
        }

        public function getAuthor() {
                return $this->author;
        }

        public function getLanguage() {
                return $this->language;
        }

        public function isPublic() {
                return $this->public;
        }

        public function getOwner() {
                return $this->owner;
        }
}

Nun erstellen wir uns ein paar Objekte, damit wir auch etwas zum Testen haben. Dazu wird ein Owner-Objekt und zwei MetaData-Objekte erstellt. Der Owner wird in beiden MetaData-Objekten verwendet, so kann man später auch die Instanzen vergleichen.

$owner = new DocumentOwner("M.", "Mustermann");

$meta1 = new DocumentMetaData(500, array("Goethe"), "Deutsch", true, $owner);
$meta2 = new DocumentMetaData(500, array("Goethe"), "Deutsch", true, $owner);

Wenn man einen PHPUnit Test schreibt würde man zum Beispiel folgende Asserts definieren. Ein Test auf Gleichheit; Objekte mit gleichem Inhalt sollten gleich sein. Und ein Test ob 2 Instanzen gleich sind.

$this->assertEquals($meta1, $meta2);
$this->assertSame($meta1->getOwner(), $meta2->getOwner());

Wer sich über das $this wundert, sollte bedenken, dass die Testklasse von PHPUnit_Framework_TestCase abgeleitet ist und von daher diese Methoden erbt.

Jetzt werden diese Assertions nach Hamcrest „übersetzt“ und sollten dadurch leichter zu lesen sein. Eine Beispiel hierfür ist folgender Code:

assertThat($meta1, equalTo($meta2));
assertThat($meta1->getOwner(), is(sameInstance($meta2->getOwner())));

Es gibt in Hamcrest nicht nur eine richtige „Formulierung“, sondern man nutzt die einzelnen Funktionen die Assertion sprachlich ausdrücken würde. In der zweiten Zeile ist das is() nur Zucker, der die Lesbarkeit verbessern soll. Ohne is würde die Assertion genauso richtig sein.

Fazit:
Einen Nachteil den ich sehe ist die Tatsache, dass PHPUnit die Anzahl der Assertions nicht mehr korrekt bestimmen kann, weil das Hamcrest-assertThat kein herkömmlicher assert*-Methodenaufruf ist. Die Ausgabe von phpunit endet somit nicht mir „OK (2 tests, 3 assertions)“, sondern würde nur die Anzahl der Tests korrekt bestimmen. Wenn ein Tool auf dieser Information aufbaut, kann es aus dem Tritt kommen.
Ansonsten ist Hamcrest ein interessanter Ansatz. Bei einem geübten „Unittester“ kann durch die fremde Syntax aber auch leichte Verwirrung entstehen.

1 Kommentar

Schreibe einen Kommentar

Pflichtfelder sind mit * markiert.