PHPUnit

Aus THM-Wiki
Wechseln zu: Navigation, Suche

PHPUnit ist eine PHP-Portierung von JUnit, dem bekannten Framework zum Testen von Java-Programmen. Dieses Unit Test Framework bietet den Rahmen für Testfälle und stellt unterschiedliche Zusicherungsmethoden zur Verfügung, mit denen geprüft werden kann, ob die zu erwartenden Ergebnisse von Methoden mit den realen Werten übereinstimmen. Neben verschiedenen Ausgabeformaten zur Protokollierung der Testfälle (XML, GraphViz, TAP) bietet PHPUnit die Möglichkeit, die Ausführungsgeschwindigkeit eines Tests zu überprüfen.

Zweck und Einsatz

Extreme Programming (XP), das zu den so genannten agilen oder leichtgewichtigen Software-Entwicklungsprozessen gehört, fordert die Einhaltung des Test-First-Ansatzes, d.h. vor der Realisierung des eigentlichen Codes müssen bereits passende Tests zu den Funktionen geschrieben worden sein. Die Folgen dieser Voraussetzung sind eine bessere Wart- und Testbarkeit des Codes. Unit Tests sind somit zu einem festem Bestandteil von Extreme Programming geworden.

Zusätzlich ist ohne den Einsatz solcher Entwicklertests auf Modulebene das Refactoring von Code nur schwer möglich. Bei solchen Änderungen, die normalerweise die innere Struktur verbessern und das externe Verhalten beibehalten sollen, können negative Seiteneffekte im restlichen Code durch den Einsatz von Unit Tests vorzeitig erkannt und korrigiert werden.

Installation

PHPUnit ist Bestandteil von PEAR (PHP Extension and Application Repository). Der sog. PEAR Installer ermöglicht einen schnellen und betriebssystemunabhängigen Installationsvorgang. Um diesen zu nutzen, muss zunächst PEAR installiert und dann (normalerweise) per Kommandozeile in das PHP-Installationsverzeichnis gewechselt werden.

Es ist empfehlenswert, sofern ein Windows-Betriebssystem verwendet wird, die Umgebungsvariable PATH um den Pfad zur ausführenden Datei von PHPUnit zu ergänzen. Diese liegt gewöhnlich unter xampp\php.

Für die eigentliche Installation müssen die Anweisungen der PHPUnit-Seite befolgt werden.

Anwendungshinweise

Szenario 1

Falls bereits eine Klasse erstellt wurde und die meisten zu testenden Funktionen eher primitiv sind, kann mit Hilfe von Annotationen im Code die Generierung einer Testklasse um ein Vielfaches beschleunigt werden.

<?php
class Taschenrechner
{
    /**
     * @assert (0, 0) == 0
     * @assert (0, 1) == 1
     * @assert (1, 0) == 1
     * @assert (1, 1) == 2
     */
    public function addieren($a, $b)
    {
        return $a + $b;
    }
}
?>

Im aufgeführten Code wird eine Klasse Taschenrechner definiert. Die darin enthaltene Funktion addieren() wurde um sog. assert-Annotationen ergänzt. Die Bestandteile eines solchen Asserts sind Beispielwerte für die Parameter der Funktionen, ein Vergleichsoperator und dem zu erwartenden Rückgabewert. Zusätzlich gibt es noch eine Variante, um Exceptions als Rückgabewert ebenfalls zu verarbeiten.
Zur Erstellung der Testklasse muss nun folgender Befehl ausgeführt werden (Die automatisch generierte Struktur mit Hilfe der --skeleton Option wird dabei um die per Annotation ergänzten Testfälle erweitert).

phpunit --skeleton Taschenrechner

Um ein besseres Verständnis für PHPUnit zu bekommen, wird der daraufhin generierte Code hier noch aufgeführt:

<?php
// Call TaschenrechnerTest::main() if this source file is executed directly.
if (!defined('PHPUnit_MAIN_METHOD')) {
    define('PHPUnit_MAIN_METHOD', 'TaschenrechnerTest::main');
}

require_once 'PHPUnit/Framework.php';

require_once 'Taschenrechner.php';

/**
 * Test class for Taschenrechner.
 * Generated by PHPUnit on 2008-02-18 at 21:03:09.
 */
class TaschenrechnerTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var    Taschenrechner
     * @access protected
     */
    protected $object;

    /**
     * Runs the test methods of this class.
     *
     * @access public
     * @static
     */
    public static function main()
    {
        require_once 'PHPUnit/TextUI/TestRunner.php';

        $suite  = new PHPUnit_Framework_TestSuite('TaschenrechnerTest');
        $result = PHPUnit_TextUI_TestRunner::run($suite);
    }

    /**
     * Sets up the fixture, for example, opens a network connection.
     * This method is called before a test is executed.
     *
     * @access protected
     */
    protected function setUp()
    {
        $this->object = new Taschenrechner;
    }

    /**
     * Tears down the fixture, for example, closes a network connection.
     * This method is called after a test is executed.
     *
     * @access protected
     */
    protected function tearDown()
    {
    }

    /**
     * Generated from @assert (0, 0) == 0.
     */
    public function testAddieren()
    {
        $this->assertEquals(
          0
,
          $this->object->addieren(0, 0)
        );
    }

    /**
     * Generated from @assert (0, 1) == 1.
     */
    public function testAddieren2()
    {
        $this->assertEquals(
          1
,
          $this->object->addieren(0, 1)
        );
    }

    /**
     * Generated from @assert (1, 0) == 1.
     */
    public function testAddieren3()
    {
        $this->assertEquals(
          1
,
          $this->object->addieren(1, 0)
        );
    }

    /**
     * Generated from @assert (1, 1) == 2.
     */
    public function testAddieren4()
    {
        $this->assertEquals(
          2
,
          $this->object->addieren(1, 1)
        );
    }
}

// Call TaschenrechnerTest::main() if this source file is executed directly.
if (PHPUnit_MAIN_METHOD == 'TaschenrechnerTest::main') {
    TaschenrechnerTest::main();
}
?>

Szenario 2

Falls für die Ausführung einer Methode nur ein beschränktes Zeitfenster zur Verfügung steht, sollte eine besondere Erweiterung einer Test-Klasse eingesetzt werden. Neben der Möglichkeit die Ausgabe via echo, print etc. zu kontrollieren (PHPUnit_Extensions_OutputTestCase) existiert noch eine Klasse zur Geschwindigkeitskontrolle, nämlich PHPUnit_Extensions_PerformanceTestCase.

<?php
require_once 'PHPUnit/Extensions/PerformanceTestCase.php';
require_once 'Taschenrechner.php';

class TaschenrechnerPerformanceTest extends PHPUnit_Extensions_PerformanceTestCase
{
  public function testAddierenPerformance()
  {
    $this->setMaxRunningTime(2);
    $tmp_calc = new Taschenrechner();
    $tmp_value = 0;
    for($i=0;$i<100000;$i++)
      $tmp_calc->addieren($tmp_value,i);
  }
}
?>

Das vorliegende Beispiel orientiert sich an der Definition der Klasse Taschenrechner aus Szenario 1. In dieser Test-Klasse wird am Anfang der Methode testAddierenPerformance() die maximale Dauer für die Ausführung sämtlicher Befehle innerhalb der Testmethode.
Eine Beispielausgabe auf der Konsole für diese Test-Klasse könnte folgendermassen aussehen, sofern eine Abarbeitung der Befehle nicht innerhalb der Maximalzeit erfolgen konnte:

...
C:\xampp\htdocs\tests\testing_l>phpunit TaschenrechnerPerformanceTest.php
PHPUnit 3.2.5 by Sebastian Bergmann.

F

Time: 2 seconds

There was 1 failure:

1) testAddierenPerformance(TaschenrechnerPerformanceTest)
expected running time: <= 2 but was: 2.46615982056

FAILURES!
Tests: 1, Failures: 1.

Code-Metriken mit PHPUnit

PHPUnit besitzt eine bereits eingebaute Möglichkeit, um zu getestetem Code Metriken zu berechnen. Diese Metriken können beispielsweise zur Analyse der Effektivität von Unit-Tests herangezogen werden.

Voraussetzungen

  • Die PHP-Erweiterung Xdebug muss installiert und eingerichtet (php.ini) sein.
sudo aptitude install php5-xdebug
  • Ein passender Unit-Test zur zu testenden Klasse muss existieren. Wenn dies nicht der Fall ist, wird ein "Dummy"-Unit-Test ohne Funktionalität erstellt.

Anwendung

PHPUnit wird aufgerufen und schreibt die Ergebnisse der Berechnungen in die Ausgabedatei.

phpunit --log-metrics <AUSGABEDATEI>.xml <TESTKLASSE> <TESTKLASSENDATEI>.php

Nun können anhand der Metriken Änderungen am Code dokumentiert (zum Beispiel im Vorher-Nachher-Vergleich), oder die Komplexität von Methoden kann quantifiziert werden (siehe auch: Pardon My French, But This Code Is C.R.A.P.

Testabdeckung mit PHPUnit

PHPUnit bietet ebenfalls die Möglichkeit, eine grafische Darstellung der Testabdeckung zu generieren. Dazu muss der folgende Befehl ausgeführt werden.

phpunit --coverage-html ./<AUSGABEVERZEICHNIS> <TESTKLASSENDATEI>.php

Nun sind im Verzeichnis <AUSGABEVERZEICHNIS> HTML-Generierungen der Testabdeckung zu finden.

Weblinks

phpunit.de PHPUnit Dokumenation (PHPUnit - kurz & gut)

Literatur

  • Sebastian Bergmann: Professionelle Softwareentwicklung mit PHP 5. dpunkt.verlag, ISBN 3-89864-229-1
  • Jack D. Herrington: PHP Hacks. O´Reilly Verlag, ISBN 3-89721-452-0