Einführung ins Testen

Bevor wir mit dem Testen loslegen können, sollten erst mal ein paar grundsätzliche Aussagen zum Testing geklärt werden. Hierdurch sollte einige Klarheit geschaffen werden, wofür Testing gut ist, was man testen kann, was man testen will und was alles dazu gehört.

Begrifflichkeiten

Test
Test eines einzelnen Verhaltens einer Funktionalität. Ein Test sollte immer so klein wie möglich sein. Beispiel: Rufe eine Funktion mit dem Parameter "5" auf und erwarte, dass 7 zurückkommt.
Test Case
Sammlung von Tests einer Funktionalität/eines Modul. Ein Test Case kann sich z.B. auf eine Funktion beziehen und einzelne Tests beinhalten wie "Rufe die Methode mit dem Parameter 0 auf, Rufe die Methode mit einem String auf. Rufe die Methode mit Null auf.
Test Suite
Sammlung von TestCases
Mocking
Benötigte Komponenten simulieren/in definierten Zustand bringen. Das kann zum Beispiel das erzeugen eines Datenbankeintrags sein. In komplizierteren Fällen könnte man aber z.B. auch eine Mail-Funktionalität simulieren (Redspark_RsMail austauschen gegen MyMail) um zu testen, ob eine Mail versendet wird.

Tests sind atomar

Atomar heißt, dass ein Test so klein wie möglich sein sollte und nur einfache, nicht komplexe Sachverhalte testet.
Negatives Beispiel: Eine Mail senden, checken ob die ankommt, noch eine senden, checken ob es sich um ein Dublette handelt.
Bessere Lösung: Ein Test, ob eine Mail ankommt und im System passiert was soll (Datenbank Eintrag). Zweiter Test, Datenbank in definierten Zustand bringen (Mocken) und dann eine Mail versenden.

Tests sind unabhängig

Ein Test darf nicht von einem anderen Test abhängen. Die Beispiele von oben sind auch hier gültig. Vor jedem Test sollten alle beteiligten Komponenten in einen definierten Zustand gebracht werden (Reset, Mocking).

Test sind deterministisch

Ein Test sollte immer unter gleichen Bedingungen ausgeführt werden. Zufallszahlen sind eine schlechte Praxis, da gefundene Fehler auf diese Weise nicht zuverlässig lokalisiert werden können. Stattdessen sollte man lieber Grenzwerte testen oder ggf. per Schleife viele Werte simulieren.

Performance

Tests dürfen lange dauern. Ein Test sollte nicht optimiert werden um Zeit zu sparen. Dadurch schleichen sich eher Fehler ein. Das Ausführen der Tests soll ja automatisiert passieren, kann also auch mal nachts durchlaufen.

Tests sind konkret

Ein Test sollte sich ruhig auf konkrete Daten beziehen. Feste Texte oder konkretes Markup im Test sind durchaus erwünscht. Somit werden z.B. Folgefehler vermieden (Kaputtes Translate) und bei Änderungen ein Bewusstsein für die Auswirkungen geschaffen.

Dokumentation, Kommentare, Sprechende Name

Tests sollten sprechende Namen haben. Am besten sollte man direkt formulieren, was man testet bzw. erwartet.
Beispiel: testExceptionOnNull() -> Man erwartet, dass eine Fehlermeldung ausgegeben wird, wenn Null als Parameter übergeben wird. Das hilft auch bei gefundenen Fehlern eine genaue Beschreibung zu erhalten.

Abstraktion ist nur bedingt gut

Durch Abstraktion kommen schnell Fehler zustande. Es besteht dort immer die Gefahr, dass der gleiche Fehler aus der Programmierung dort ebenfalls gemacht wird. Deshalb sollte man auf Abstraktion weitestgehend verzichten. Einfache Schleifen sind ok, Vererbung und komplexere Strukturen sollten vermieden werden.

Eigene Asserts

Eigene Asserts sind hilfreich, um präzise und sprechend Erwartungen zu formulieren. Beispiel:
assertNotNull() statt assertFalse(is_null(...))

Drei Schritte eines Tests

Ein Test besteht in der Regel aus drei Phasen:
  1. Setup: Mocking, Daten in definierten Zustand bringen
  2. Aufruf: z.B. das Dispatchen einer Action, oder der Aufruf einer Methode
  3. Erwartungen: Asserts formulieren. Formulieren, was hätte passieren sollen. Am besten auch mit sprechenden Meldungen.

Testbarkeit

Damit der Code sinnvoll getestet werden kann, sollten bereits bei der Programmierung bestimmte Regeln eingehalten werden.

Feste Abhängigkeiten sind gift:

<?php
$mail 
= new Zend_Mail();
?>

Abhängigkeiten via Broker sind mockbar:
<?php
Broker 
= new Broker_Mock();

Broker::setMail(new Redspark_Mail_Mock());

$mail Broker::newMail();
?>


Beste, aber komplexeste Lösung - Dependency injection
<?php
$x
->setMail(new Redspark_Mail_Mock());

$mail $this->getMail();
?>


Was tun, wenn der Code nicht testbar ist?
Man sollte an entsprechender Stelle im Test beschreiben, warum es nicht testbar ist.
Beispiel: Tests, die nicht geschafft wurden zu implementieren, z.B. weil das Mocking zu komplex ist.
$this->markIncomplete();

Wenn Tests nicht möglich sind und ggf. der Code umgebaut werden muss.
$this->markSkipped();



Kuborgh GmbH

Hamburg 040 819 773 770 Köln 0221 276 66 96 info@kuborgh.de www.kuborgh.de

RedSpark Community

RedSpark Community

Community Website
RedSpark Apps

RedSpark Apps

Zur Übersicht
RedSpark Download

RedSpark Basispaket

Zum Download
Key facts