VfsStream

Aus THM-Wiki
Version vom 6. Dezember 2010, 10:34 Uhr von Cthl58 (Diskussion | Beiträge) (Installation)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu: Navigation, Suche

"vfsStream is a stream wrapper for a virtual file system that may be helpful in unit tests to mock the real file system. It can be used with any unit test framework, like PHPUnit or SimpleTest" [1]. Ein reiner Unit Test darf keinerlei Kontakt mit der "Außenwelt" aufnehmen. Das bedeutet zum Beispiel, dass er nicht auf das Dateisystem zugreifen darf. Denn die Zugriffe dauern unter Umständen zu lange und wir können das Dateisystem nie zu 100% kontrollieren. Dies kann zu unerwarteten Testfehlschlägen führen.

Nicht immer kann man jedoch einen reinen Unit Test schreiben. Klassen, welche direkt auf das Dateisystem zugreifen, müssen ebenfalls getestet werden und hier bringt das Paket vfsStream die Rettung: Es bietet ein virtuelles Dateisystem an, welches nur im Speicher existiert. Über dieses Dateisystem hat man die komplette Kontrolle und die üblichen Operationen wie zum Beispiel mkdir() funktionieren nahtlos.

Dieser Artikel bringt vfsStream und dessen Verwendung näher. Er basiert auf der Erfahrung vom Einsatz in eStudy.

Installation

vfsStream wird von eStudy in dem PEAR-Phar-Archiv mitgeliefert, eine lokale Installation ist also nicht unbedingt notwendig. Wer dies jedoch trotzdem tun möchte, kann dies auf der Kommandozeile mit Hilfe von PEAR erledigen:

$ pear channel-discover pear.php-tools.net
$ pear install pat/vfsStream-beta

Verwendung

Die offizielle Seite des Pakets bietet derzeit keine offizielle API-Dokumentation an. Daher haben wir mit Doxygen eine eigene Dokumentation erstellt [2]. Diese basiert auf der Version 0.8.0 (Stand: 03.12.2010).

PHPUnit

vfsStream wird üblicherweise in der setUp()-Methode der Testklasse initialisiert. Man registriert zunächst das Paket über vfsStreamWrapper::register() und legt danach das Wurzelverzeichnis fest mit vfsStreamWrapper::setRoot(new vfsStreamDirectory("wurzel")). Beides kann aber auch mit nur einem einzigen Befehl erledigt werden: vfsStream::setup("wurzel"). Eine minimale Testklasse sieht demnach wie folgt aus:

require_once "vfsStream/vfsStream.php";

class vfsStreamTest extends PHPUnit_Framework_TestCase {
    public function setUp() {
        vfsStream::setup("wurzel");
    }
}

Um dieses virtuelle Dateisystem nun verwenden zu können, müssen Dateien und Verzeichnisse mit einer URL-Syntax angesprochen werden. Das "wurzel"-Verzeichnis im obigen Beispiel hat somit die URL "vfs://wurzel". Diese URL muss man jedoch nicht selbstständig eintippen, denn vfsStream bietet dafür bereits die passende Methode: vfsStream::url("wurzel") erzeugt die zuvor genannte URL.

Jetzt kann zum Beispiel eine virtuelle Datei erzeugt werden: Hierzu erstellen wir die erste Testmode für unseren PHPUnit-Test. Nachdem die Datei "test.txt" dort erstellt wurde, überprufen wir, ob diese Datei auch tatsächlich im virtuellen Dateisystem angelegt wurde. vfsStream bietet hierfür wieder die passende Methode: vfsStreamWrapper::getRoot()->hasChild("test.txt"). Des Weiteren können wir uns auch den Typ von "test.txt" geben lassen, um zu prüfen, ob es sich auch tatsächlich um eine Datei handelt (und nicht um ein Verzeichnis): vfsStreamWrapper::getRoot()->getChild("test.txt")->getType() liefert den Typ und diesen können wir vergleichen mit Hilfe von vfsStreamContent::TYPE_FILE. Die gesamte Testmethode jetzt im Überblick:

public function testShouldCreateFile() {
    file_put_contents(vfsStream::url("wurzel") . "/test.txt", "inhalt");
    
    $this->assertTrue(vfsStreamWrapper::getRoot()->hasChild("test.txt"));
    $this->assertEquals(vfsStreamContent::TYPE_FILE, vfsStreamWrapper::getRoot()->getChild("test.txt")->getType());
}

Nach dem gleichen Prinzip überprüfen wir das Erstellen eines Verzeichnisses. Diesmal verwenden wir zum Vergleich den Aufruf vfsStreamContent::TYPE_DIR:

public function testShouldCreateDirectory() {
    mkdir(vfsStream::url("wurzel") . "/dir");
    
    $this->assertTrue(vfsStreamWrapper::getRoot()->hasChild("dir"));
    $this->assertEquals(vfsStreamContent::TYPE_DIR, vfsStreamWrapper::getRoot()->getChild("dir")->getType());
}

Im nächsten Schritt testen wir, ob wir auch innerhalb von neu erstellten Verzeichnissen weitere Dateien erzeugen können. Die Methode getChild("dir") liefert uns ein Objekt des Typs vfsStreamDirectory. Mit dessen getChildren()-Methode können wir direkt z.B. die Anzahl der Dateien im Verzeichnis bestimmen:

public function testShouldCreateFilesInDirectory() {
   $dir = vfsStream::url("wurzel") . "/dir";
   mkdir($dir);
   file_put_contents($dir . "/1.php", "");
   file_put_contents($dir . "/2.php", "");
   file_put_contents($dir . "/3.php", "");
   
   $directory = vfsStreamWrapper::getRoot()->getChild("dir");
   $this->assertEquals(3, count($directory->getChildren()));
   $this->assertTrue($directory->hasChild("1.php"));
   $this->assertTrue($directory->hasChild("2.php"));
   $this->assertTrue($directory->hasChild("3.php"));
}

Einschränkungen

Es gibt einige PHP-Funktionen, die nicht auf die von vfsStream verwendete Stream-Funktionalität achten. Dazu gehört zum Beispiel die Funktion dirname() oder die ZIP-Funktionalität der Erweiterung ext/zip. Hier gibt es leider keine Möglichkeit, vfsStream direkt einzusetzen. Sollten also beim Verwenden von vfsStream die Ergebnisse nicht so ausfallen, wie man dies erwartet hat, dann liegt dies mit hoher Wahrscheinlichkeit daran, dass vfsStream ignoriert wird.

Im Falle der Zip-Erweiterung lässt sich jedoch der Einsatz von vfsStream ermöglichen, indem man die Zip-Klasse (ZipArchive) beispielsweise durch ein Mock-Objekt ersetzt.

Weiteres