PHP Unittesting + Coverage + XDebug

joschilein

Multitalent
ID: 9301
L
5 Mai 2006
1.393
151
Ich versuche gerade Simpletest in ein Projekt einzubinden (besser spät als nie :ugly:), was soweit auch funktioniert. Die Tests laufen und bringen die gewünschten Ergebnisse. Nur möchte ich eben auch so früh wie möglich im Testaufbau erkennen können, welche Bereiche überhaupt (schon) mit den Tests abgedeckt wurden.

Zwar hat Simpletest in der Dokumentation eine Extension namens CodeCoverage, aber auch in der 1.1alpha3 ist bei den Dateien nichts zu finden.

Mit PHPUnit habe ich mal eine Weile geliebäugelt, das kommt mir aber nicht auf den Server. Einzig hierfür habe ich aber einen Visualiser gefunden, der aber natürlich nicht eigenständig lauffähig ist.

Ich habe auch versucht mich in die xdebug-Funktionen einzufinden, aber deren Verhalten ist noch etwas gewöhnungsbedürftig, zumal ich das Rad ja quasi neu erfinden müsste. Spontan würde die über xdebug_get_code_coverage() gefundenen Zeilennummern mit einer zeilenweisen Analyse des jeweiligen php-Quelltextes abgleichen, um herauszufinden welche Klasse/Methode/Funktion in welcher Zeile anfängt und was dementsprechend benutzt wurde oder nicht. Und dann würde ich mich vermutlich rumärgern, weil leere oder Kommentarzeilen gar nicht als benutzt erscheinen..

Gibt es also irgendwo eine eigenständige lauffähige Klasse um xdebug vernünftig auszuwerten?
 
Mit PHPUnit habe ich mal eine Weile geliebäugelt, das kommt mir aber nicht auf den Server. Einzig hierfür habe ich aber einen Visualiser gefunden, der aber natürlich nicht eigenständig lauffähig ist.

PHPUnit ist der PHP-Standard für UnitTests, von daher würde ich diese Entscheidung nochmal überdenken ;)
Was stört dich denn daran? Code-Coverage mit Xdebug geht da auch ohne, dass man irgendetwas selbst machen muss. Das man 3 Jahre (!!!) brauchte um für SimpleTest eine neue Version herauszubringen und diese dann sogar nur eine Alpha ist, würde ich dieses Projekt als alles andere als aktiv bezeichnen.
 
Ich bekomme PHPUnit einfach nicht in Gang. Das mag an einer anderen Abneigung liegen, nämlich dieser pear-geschuldeten Pfadstruktur. Meine Vorliebe geht Richtung relativer Pfadangaben - absolute Pfade oder gar "globale" Pfade sind mir total unsympatisch.

Die Einbindung eines externen Tools stelle ich mir so vor:
PHP:
// 1) ggf. noch vorher Konstanten oder Variablen definieren..
$phpunitDir = '../externes/phpunit-v12.34.5/';
// 2) Einbinden
include_once($phpunitDir.'Autoloader.php');
// 3) Fertig. Die benötigten Klassen stehen bereit.

Wenn ich mich aber durch quasi jede Datei eines Projektes wühlen muss um manuelle Änderungen zu machen und sofort schon weiß wie nervig das wieder wird, wenn ich mal eine neuere Version benutzen möchte, dann vergeht mir an sowas gleich zu Beginn die Lust. Oder anders ausgedrückt: Wenn ich mal anfange mehr als 2-3 notwendige Eingriffe zu machen, kann es sein dass ich mich total in dem "och da könnt ich doch auch noch" verfange und es am Ende gleich hätte neu schreiben können.
 
Eigentlich ist es viel einfacher ;)
Diese manuelle Angabe des PHPUnit-Ordners brauchst du nicht, dein Pear-Ordner ist bei jeder PHP-Installation automatisch im include_path.


einfach per pear installieren:
Code:
pear config-set auto_discover 1
pear install pear.phpunit.de/PHPUnit

dann einen Test schreiben:
PHP:
require_once 'PHPUnit/Autoload.php';
require_once 'Calculator.php'; // Sollte man natürlich vorher implementieren ;)

class CalculatorTest extends PHPUnit_Framework_TestCase {
  public function test2Plus2Is4() {
     $this->assertSame(4, Calculator::add(2, 2));
  }
}

und zum Schluss mal den UnitTest ausführen:
Code:
phpunit --coverage-html "./coverage" CalculatorTest.php

Das war es schon!
Deswegen fragte ich, warum es dir so missfällt. Es ist extrem einfach installiert, und der erste Test ist auch in wenigen Sekunden geschrieben. Und es macht eben keine Probleme.
Wenn dir ein Runner für den Browser besser gefällt, geht das auch, sind aber eben 5 Zeilen mehr Code.

Wenn du PHPUnit nutzt, hast du auch den Vorteil das Jenkins PHP-Template nutzen zu können und mittels Jenkins (Continous Integration Server) bei jedem Commit, FileChange, Build-Prozess oder was auch immer deine Software Testen, Packen und Publishen zu können.
 
Mir ist schon klar, dass das Konzept von pear ist. Und genau da liegt mein Problem. Wenn ich jetzt auf die Schnelle richtig nachgeschaut habe, hat sich das mit der Path-Angabe auf Systemebene nicht geändert. Jedenfalls war das noch so, als xampp mal 25 php.ini's hatte.

Lokal nutze ich Xampp Portable und das immer mal an anderen Computern. Ich bin schon froh, wenn ich Apache halbwegs vernünftig konfiguriert habe, alle notwendigen php-Extensions laufen und sich Apache nicht nach dem Start sofort wieder verabschiedet. Ich werde verrückt, wenn es dann ständig noch zusätzliche Systemabhängigkeiten gibt und sei es nur wegen wechselnden Laufwerksbuchstaben.

Bekomme ich pear also auch ohne Systemanpassung und ohne Laufwerksangabe hin? Spontan habe ich nichts gefunden, was meine ursprünglichen Befürchtungen (vielleicht sind sie wirklich übertrieben, aber so ist es nunmal) verschwinden lassen könnte.
 
Ich weiß nicht wie Xampp Portable die dauernden Laufwerksbuchstaben behandelt, was passiert denn, wenn du einfach mal versuchst PHPUnit zu installieren und zu nutzen?
Ansonsten installiere es eben nur an einem Rechner und kopiere dir dann alle Dateien in einen Ordner deines Projektes.

Durch Xampp Portable sind eben viele Probleme hausgemacht.
 
Nö geht sogar besser als gedacht. Neuerdings* ist pear sogar vorinstalliert und der include_path ist ohne Laufwerksbuchstaben. Für PHPUnit musste ich jetzt zwar eine neue Version und noch ein paar Abhängigkeiten nachinstallieren, aber grundsätzlich ist es machbar.

AUßER...
Wenn dir ein Runner für den Browser besser gefällt, geht das auch, sind aber eben 5 Zeilen mehr Code.
Wie bekomme ich jetzt eigentlich meine Ergebnisse zu sehen. Ich habe schon seit Stunden nebenbei Manual und Google maltretiert und nichts gescheites gefunden :(


*Wie gesagt habe ich schon länger nicht mehr geprüft, ob sich da was geändert haben könnte.
 
Wie bekomme ich jetzt eigentlich meine Ergebnisse zu sehen. Ich habe schon seit Stunden nebenbei Manual und Google maltretiert und nichts gescheites gefunden :(

Kommandozeile *duck*
(habe ich oben gezeigt)

Wenn es dir aber nur um die Ausgabe geht, kann man auch den PHP-Runner aktivieren:
PHP:
<?php

// allows running phpunit tests in browser
if (!defined('PHPUnit_MAIN_METHOD'))
    define('PHPUnit_MAIN_METHOD', 'BcryptTest::main');

require_once 'PHPUnit/Autoload.php';

class BcryptTest extends PHPUnit_Framework_TestCase {
    public static function main() {
        $suite  = new PHPUnit_Framework_TestSuite('BcryptTest');
        $result = PHPUnit_TextUI_TestRunner::run($suite);
    }
}

// runs all tests when opened in browser or with cli
if (PHPUnit_MAIN_METHOD == 'BcryptTest::main') {
    BcryptTest::main();
}

So hab das jetzt mal aus nem Projekt ausgeschnitten, die ganze Doku (Doc-Blocks) entfernt und alle Tests rausgenommen, ich denke da ist ersichtlich wie du den PHP-Runner aktivierst.
 
PHPUnit auf Kommandozeile

Hallo joschilein,

in meinen Augen hat ice-breaker mit dem Hinweis auf die Kommandozeile etwas total wertvolles angemerkt: Ich habe PHPUnit in meinem vorletzten Projekt massiv genutzt und es war Gold wert. Ich habe mir selbst die Arbeitsumgebung so organisiert, dass ich immer auch auf der Kommandozeile arbeiten kann. Unter Mac OS X eh kein Problem. Unter Windows habe ich mir den WAMPserver installiert, sowie Cygwin und MinTTY. Als ich dann anegfangen habe mit einem Freund gemeinsam an dem Projekt zu arbeiten, haben habe ich außerdem einen VSever gemietet und wir beide haben alle Tests nur noch auf dem Server gemacht, um einen einheitlichen Bezugspunkt zu haben. War Aufwand, hat sich aber total gelohnt:

An PHPUnit ist unter anderem auch so genial, das es Abhängikeiten unterstützt. Wenn in einem größeren Projekt mal in einer grundlegenden Funktion ein Fehler ist, kommen viele Fehlemreldungen zu Folgefehlern. Wenn die Abhängigkeiten korrekt markiert sind, bleibt alles übersichtlich ...

Wenn Deine Skripte in einem Ordner ProjektOrdner liegen und Deine Tests in ProjektOrdner\Tests, kannst Du auf der Komamndozeile alle Tests ausführen mit

cd ProjektOrdner
phpunit Tests

Wenn Du gerade genau weisst, das alles O.K. ist aber nur die eine Klasse Probeleme macht, kannst Du auch gezielt nur die testen:

phpunit Tests\DieEineKlasse.php

Ich hab' selbst die Einrichtung einer guten Arbeitsumgebung eine Weile vor mir hergeschoben. Letztendlich habe ich mich dann aber doch aufgerafft. Dazu möchte ich Dich ermutigen. Ja, war Arbeit. Und sie hat sich doppelt und dreifach gelohnt.

Freudige Grüße!

Timon
 
Hmm also ich hänge immer noch etwas, auch weil ich im Manual einfach keine gescheiten Antworten darauf finde :-?

Mein Dateiaufbau sieht grob gesagt so aus:
  • Für jede grundlegende Aufgabe gibt es eine Arbeitsklasse in einer einzelnen Datei. Deren Dateiname hat die Form "class.klassenname.php".
  • Keine Arbeitsklasse macht ungefragt eigene Ausgaben (oder sie werden gebuffert).
  • Es gibt nur zwei Einstiegsseiten (backend.php, frontend.php), die alles gewollte anstoßen und auch die jeweiligen Arbeitsklassen einbinden. htaccess leitet auf diese beiden Dateien weiter. Zusätzlich sind die Dateien der Arbeitsklassen davor geschützt (die()) einzeln aufgerufen zu werden.
  • Nun möchte ich für jede Arbeitsklasse eine Testklasse schreiben. Diese darf als Quasi-Doku gerne in der selben Datei stehen (s.u.).
  • Die Ausgabe der Tests soll erstmal nur in einem speziellen Teil im Backend erfolgen und keinesfalls im Frontend

class.A.php
PHP:
class A{
  static function double($x){
    return $x*2;
  }
}

class TestA extends ... {
  function test1(){
    $this->assertSame(4, A::double(2));
  }
}

class.backend.php
PHP:
class Backend{ 
//...  
  function showTests(){    
    // ??????? 

    // * Eigentliches Anstoßen der Tests
    // * Optische Aufbereitung der Ergebnisse
    // ** Tabelle (=Auflistung der Tests) mit viel grün und wenig rot
    // ** ggf. ausgelagert in einem eigenen Runner
    // ** Abgreifen der reinen Summen (z.B. 423 Tests, 422 Ok, 1 Fails)
    // * Als Ziel, kurz gesagt, Ausgabe per echo
  }
//...
}

Nur wie bekomme ich das nun hin? Simpletest habe ich so einbinden können, bei PHPUnit stehe ich irgendwie auf dem Schlauch. Ob die Tests dann letztlich in der selben Datei sind, ist zweitrangig, da kann ich auch gerne einen Ordner tests machen und dort pro Arbeitsklasse eine Testklasse hinterlegen. Hauptsache das Anstoßen der Tests kann sinnvoll gesteuert werden und die Ergebnisdarstellung ist ansprechend und lässt sich ins Backend integrieren. Kommandozeile... naja. Ich will das eben auch einsehen können, wenn ich nur einen simplen Browserzugang an fremden Rechnern habe.
 
Da du scheinbar einen eigenen TestRunner bauen willst, wirst du dich da mal einlesen müssen. Wie man den PHP-Runner anstößt, habe ich ja bereits oben gezeigt. Diesen kannst du also als Grundlage nutzen.

Nebenbei bietet die Kommandozeilen-Variante viele Ausgabe-Formate, du kannst dir z.B. einen kompletten Report als XML ausgeben lassen, da steht dann jeder Test samt Ergebnis drinne, diesen müsstest du nur noch parsen.


Allerdings erscheint mir das alles sehr suspekt! UnitTests werden immer durchgeführt, bevor das Projekt deployed wird, von daher wirst du nie die UnitTests auf einem produktiven Server finden, die haben da einfach nichts zu suchen.
 
Dann sag mir mal bitte wo ich anfangen soll zu lesen. Ich habe das Manual schon ein paar Mal hoch und runter quergelesen und keinen gescheiten Einstieg gefunden.

Und natürlich dienen die Tests nicht insbesondere dem Produktivsystem. Aber letztlich hängen viele Dinge eben auch von den Servereinstellungen bzw. -voraussetzungen ab und auch dafür möchte ich im Hintergrund des Produktivsystems die Tests laufen haben, um eben solche unerwünschten Veränderungen schneller feststellen zu können. Für manches habe ich mir natürlich schon große Warnungen gebastelt (z.B. bei fehlenden Schreibrechten im Cache-Verzeichnis), aber das könnte ja auch einfach in die anderen Tests integriert werden und das Gefrickel an verschiedenen Stellen entfällt. Dann gibt es noch die paar Dinge, die ich offline nicht gebacken bekomme, z.B. das kürzliche Thema Schlüsselerstellung, was ich dann eben erst online richtig testen kann.

Als Entwarnung sei auch noch erwähnt, dass ich nie direkt im Produktivsystem rumspiele. Ich habe für jede Version des Gesamtsystems einen separaten Hauptordner und kann die über htaccess sauber umschalten. Wenn also eine neue Version hochgeladen wird, laufen die Produktivanfragen weiterhin auf die alte Version und die neue kann nebenbei, zusätzlich zu den vorherigen Offline-Tests, getestet werden.
 
Da kann ich dir nicht helfen, der Sinn hinter PHPUnit ist nicht, dass sich jeder einen eigenen Runner baut, von daher steht so etwas auch nicht im Manual. Jeder andere würde dann auch einfach PHPUnit auf dem Deployment-Server auf der Kommandozeile ausführen...
 
...
Als Entwarnung sei auch noch erwähnt, dass ich nie direkt im Produktivsystem rumspiele. Ich habe für jede Version des Gesamtsystems einen separaten Hauptordner und kann die über htaccess sauber umschalten. Wenn also eine neue Version hochgeladen wird, laufen die Produktivanfragen weiterhin auf die alte Version und die neue kann nebenbei, zusätzlich zu den vorherigen Offline-Tests, getestet werden.

Eine räumliche Trennung von Test- und Produktivsystem sollte man immer einhalten.
Welche speziellen Tests willst Du denn haben, bzw. was willst Du denn überwachen?