In diesem Tutorial möchte ich erklären, wie man in TYPO3 Flow einen UnitTest mit PHPUnit durchführen kann. Dies zeige ich an dem Beispiel einer Klasse Palindrom. Unter einem Palindrom (Wortpalindrom) wird ein Wort verstanden, in dem die einzelnen Buchstaben von vorne wie von hinten gleich angeordnet sind. Ein Beispiel hierfür wäre Otto oder Lagerregal.

Vorausschicken möchte ich, dass ich das Tutorial mit der Flow-Version 2.0 getestet habe. Die Installation von phpUnit habe ich über den Composer vorgenommen. Wie ihr das macht, könnt ihr unter folgendem Link nachsehen: PHPUnit mit dem Composer installieren

So weit so gut. Beginnen wir mit dem Tutorial.

Zu Beginn installiere ich ein neues Package in Flow:

./flow kickstart:package TYPO3.UnitTestBeispiel

In einem zweiten Schritt erhält unser Package ein Model und eine Testklassse wird angelegt.

./flow kickstart:model --force TYPO3.UnitTestBeispiel Palindrom wort:string

Nun öffnet man das Model, welches bei mir unter:

P { margin-bottom: 0.21cm; }

/Package/Application/TYPO3/UnitTestBeispiel/Classes/Model/Palindrom.php

liegt. Die Klasse Palindrom enthält in Ihrem Ursprungszustand die von uns generierte Property $wort sowie die Methoden setWort und getWort.

P { margin-bottom: 0.21cm; }

Für unser Beispiel benötigen wir weder die set noch die get Methode, so dass wir diese löschen können. Nun fügen wir folgenden Code hinzu. Dies ist zum einen der Konstruktor, welcher später unsere Klasse instanziert sowie die Methode isPalindrom(), welche die Prüfung unseres Wortes auf das Vorhandensein eines Palindroms hin übernimmt. Hier der Code der Klasse Palindrom.php

/*
 * Constructor
 */
public function __construct() {}

/**
 * @var return
 */
public function isPalindrom ($wort) { 
    //check if $wort is of type string     
    if(!is_string($wort)) {
        throw new \InvalidArgumentException('Invalid argument');            
    } else {        
        $originalString = strtolower($wort);       
        $reversedString = strrev($originalString);       
        //compare two strings
        if(strcmp($originalString, $reversedString) == 0) {
            return TRUE;
        } else {
            return FALSE;
        }
    }
}

Nun will ich einen UnitTest für diese Klasse durchführen. Im engeren Sinne bedeutet dies, das wir die Methode isPalindrom mit Testdaten füttern und je nach dem TRUE oder FALSE zurückgegeben wird. Wenn ihr euch schon ein wenig mit demTesten von Klassen auseinandergesetzt habt, dann ist euch bestimmt aufgefallen, dass man hierfür einen spezielle Testklasse erstellen muss. In Flow wird euch diese bereits beim kickstarten des Models angelegt. Bei mir liegt diese unter:

/Package/Application/TYPO3/UnitTestBeispiel/Tests/Unit/Domain/Model/PalindromTest.php

Auch diese Datei enthält bereits Code, jedoch können wir damit in unserem Fall nichts anfangen. Eure Datei sollte nach dem säubern wie folgt aussehen:

/**
 * Testcase for Palindrom
 */
class PalindromTest extends \TYPO3\Flow\Tests\UnitTestCase {

}

Nun können wir unseren Test aufbauen.

P { margin-bottom: 0.21cm; }

Zuerst schreiben wir eine Methode makeSureThatWordIsAPalindrom(). In dieser initialisieren wir ein Objekt der zu testenden Klasse Palindrom. Danach rufen wir die Methode isPalindrom() z.b. mit dem String "Otto" auf, welche uns entweder True oder False zurückgibt. Um ein Testergebnis zu erhalten, bedienen wir uns der PHPUnit Methode assertTrue(). Demnach ist der Test TRUE, wenn ein Palindrom endeckt wurde. Ist das Wort kein Palindrom liefert uns der Test einen Fehler.

Hier der Code: P { margin-bottom: 0.21cm; }

public function makeSureThatWordIsAPalindrom($wort) {
    $a = new \TYPO3\UnitTestBeispiel\Domain\Model\Palindrom();        
    $b = $a->isPalindrom("Otto");		
    $this->assertTrue($b);	 			
}

Nun speichern wir die Klasse PalindromTest und, falls noch nicht vorgenommen, auch die Klasse Palindrom.php und kehren zurück in die Kommandozeile.

Um den Test auszuführen und hoffentlich ein brauchbares Ergebnis zu erhalten, ob sich hinter dem übergebenen Wort ein Palindrom versteckt oder nicht, müssen wir folgenden Befehl ausgehend vom Rootverzeichnis unserer Flow-Installation eingeben:

P { margin-bottom: 0.21cm; }

root@ThinkPad:/var/www/flowblog# phpunit -c Build/BuildEssentials/PhpUnit/UnitTests.xml Packages/Application/TYPO3.UnitTestBeispiel/Tests/Unit/Domain/Model/PalindromTest

Wir erhalten folgende Ausgabe:

Time: 5 ms, Memory: 5.00Mb
OK (1 test, 1 assertion)

Demnach ist unser Wort "Otto" ein Palindrom.

Da ich in einem Testdurchlauf mehrere Worte auf ein Palindrom hin überprüfen möchte, bietet sich ein Array mit Testwörtern an. Um dies zu realisieren öffnen wir wieder die Klasse PalindromTest.php erneut und verändern diese wie folgt.

Für unsere Testwörter erstellen wir eine Methode isValidPalindrom() und fügen dieser vorerst drei Einträge hinzu. Hierzu der Code.

/**
 * @return array
 */
 public function isValidPalindrom () {
     return array(           
         array('Otto'),
         array('Lagerregal'),
         array('Frühling') 
     );
 }

Führen wir nun einen Test aus, so würde der Test dreimal durchlaufen und jedes der Wörter wird auf ein Palindrom überprüft. Jedoch Vorsicht! Noch weis unsere Methode makeSureThatWordIsAPalindrom() nichts von den drei Wörtern. Um Abhilfe zu schaffen, müssen wir eine Annotation über der Methode hinzufügen, sowie der Methode einen Parameter hinzufügen. Dem zu Folge wird die Methode isPalindrom() um den Parameter $wort erweitert. Unsere Klasse PalindromTest sollte nun wie folgt aussehen:

/**    
 * @dataProvider isValidPalindrom
 * @test
 */
public function makeSureThatWordIsAPalindrom($wort) { 
    $a = new \TYPO3\UnitTestBeispiel\Domain\Model\Palindrom();        
    $b = $a->isPalindrom($wort); 
    $this->assertTrue($b);   
}

Schauen wir uns doch mal das Testergebnis an. Hierzu wieder in die Kommandozeile. Befehl wie oben:

root@ThinkPad:/var/www/flowblog# phpunit -c Build/BuildEssentials/PhpUnit/UnitTests.xml Packages/Application/TYPO3.UnitTestBeispiel/Tests/Unit/Domain/Model/PalindromTest

Voila! Nun erhalten wir das Ergebnis, dass 3 Tests durchgeführt wurden und ein Fehler besteht, da Frühling kein Palindrom ist.


TYPO3\UnitTestBeispiel\Tests\Unit\Domain\Model\PalindromTest::makeSureThatWordIsAPalindrom with data set #2 ('Frühling') Failed asserting.

FAILURES!
Tests: 3, Assertions: 3, Failures: 1.

Nun könnt ihr beliebig mit den data sets herumspielen und auf ein Palindrom hin testen. Viel Spass!

Das wars aber noch nicht ganz. Wie ihr der Klasse Palindrom entnehmen könnt, prüfen wir die Variable $wort mit is_string(). Dies fangen wir mit einer InvalidArgumentException ab. Wenn wir einen Test z.b. mit einer Zahl durchführen, bekommen wir einen Fehler und der Test kann nicht durchgeführt werden. Dies können wir mit folgendem Code bewerkstelligen:

/**  
 * @dataProvider isValidPalindrom
 * @test
 */
public function makeSureThatWordIsAPalindrom($wort) {      

    $a = new \TYPO3\UnitTestBeispiel\Domain\Model\Palindrom();      
    $b = $a->isPalindrom($wort);
  
    try{  
        $this->assertTrue($b);
    }catch(\TYPO3\Flow\Exception\Aop\Exception\InvalidArgumentException $exception) {
         return;      
    }                  
}

Nun läuft der Test auch mit der Übergabe einer Zahl (bei mir mit der Zahl 5) reibungslos durch ...with data set #2 (5)
InvalidArgumentException: Invalid argument

FAILURES!
Tests: 3, Assertions: 2, Errors: 1.

Last but not least, wenn ihr eine grafische Ausgabe eures Testdurchlaufs erstellen möchtet, dann müsst ihr den Befehl wie folgt ausführen:

P { margin-bottom: 0.21cm; }

root@ThinkPad:/var/www/flowblog# phpunit -c Build/BuildEssentials/PhpUnit/UnitTests.xml --coverage-html Build/Reports/UnitTestsCoverage Packages/Application/TYPO3.UnitTestBeispiel/Tests/Unit/Domain/Model/PalindromTest

Das Ergebnis findet ihr unter: /Build/Reports/UnitsTestCoverage/index.html

Solltet ihr die Fehlermeldung "The Xdebug extension is not loaded. No code coverage will be generated" bekommen, so müsst ihr einfach xdebug in der php cli pflegen. Bei mir liegt diese unter /etc/php5/cli/php.ini

Für eure Aufmerksamkeit möchte ich mich bedanken und freue mich, wenn ich dem Einen oder Anderen helfen konnte.