© saicle/Shutterstock.com
Maßgeschneiderte Testdaten mit Faker und Alice

Alice im Fakerland


Bei der Entwicklung datengestützter Webanwendungen stellt sich schnell die Frage nach passenden Testdaten – den so genannten Fixtures. Realitätsnah und zufällig verteilt sollten sie sein, gleichzeitig müssen sie der Geschäftslogik unterliegen und für Entwickler wartbar, skalierbar, leserlich und reproduzierbar zu laden sein. Zwei Bibliotheken meistern alle diese Anforderungen: Faker und Alice.

Sicherlich hat jeder Entwickler bereits auf vielfältige Art und Weise Testdaten für seine Applikationen erstellt und dabei vermutlich mehr als einmal eigene Verfahren erdacht, diese zu erzeugen und in die Datenbank zu laden. Mit einem Mix aus PHP-, SQL- und Shell-Skripten sowie der Datenhaltung in CSV-, XML- oder YAML-Daten ist natürlich alles möglich. Die Erstellung von Fixtures genießt aber oft wenig Priorität, und die Pflege und Erweiterung wird schnell lästig. Warum also keine professionelle Lösung nutzen, um in weniger Zeit ein besseres Ergebnis zu erhalten?

Dieser Artikel stellt die zwei PHP-Bibliotheken Faker [1] und Alice [2] vor. Gemeinsam bilden sie eine ausgereifte Lösung für die beschriebenen Anforderungen. Faker verfügt über simple Methoden zur Generierung von Testdaten aller Art – zum Beispiel Namen, E-Mail-Adressen, Texte, Postleitzahlen, Telefonnummern, Barcodes, Datumswerte, IPs, Kreditkartennummern und viele mehr. Alice erlaubt es, die Attribute von Testentitäten in gut lesbarem YAML-Code zu beschreiben und in echte PHP-Objekte zu übersetzen. Die Objekte lassen sich anschließend mithilfe eines Object-Relational Mappers (ORM) wie Doctrine oder Propel in einer Datenbank persistieren. Alice integriert Faker, um Objektattribute bequem mit den gewünschten Testdaten zu belegen, und bietet etliche Annehmlichkeiten etwa zur Skalierung der Fixtures, zum Umgang mit Objektreferenzen und vieles mehr. Abschließend zeigt der Artikel mit dem AliceBundle [3] eine einfache Integration von Faker und Alice in das beliebte Symfony-Framework.

Faker – realistische Testdaten

Versetzen wir uns zunächst in die Situation, Fixtures selbst zu schreiben. Die erste Herausforderung ist die Beschaffung real wirkender und konsistenter Daten. Dafür lassen sich zum Beispiel lustige Namenslisten [4] von „Klara Fall“ bis „Rainer Zufall“ kopieren. Zur Erzeugung eindeutiger Datensätze wie etwa einer E-Mail-Adresse kann ein Schleifenindex herangezogen werden; für Daten wie Straßennamen, Postleitzahlen, hexadezimale Farbcodes oder Zeitzonen hilft mit etwas Überlegung der passende PHP-Codeschnipsel. Ein Lorem-Ipsum-Generator [5] liefert Blindtexte gewünschter Länge, ein Lorem-Pixel-Generator [6] Testbilder gewünschter Größe. Kombiniert mit PHP-Logik lassen sich optionale Attribute mit gewisser Wahrscheinlichkeit setzen oder Dopplungen in den Datensätzen vermeiden. Datums­arithmetik sorgt zudem für konsistente Datumswerte. Listing 1 zeigt einige Beispiele anhand einer fiktiven Userentität.

Listing 1

$names = ['Klara Fall', 'Rainer Zufall'/*, ...*/]; shuffle($names); $timezones = \DateTimeZone::listIdentifiers(); $loremIpsum = 'Lorem ipsum dolor sit amet, consetetur ...'; $skills = ['PHP', 'JavaScript', 'SQL', 'NoSQL', 'Linux'/*, ...*/]; $users = []; for ($i = 0; $i < 10; $i++) { $userSkills = []; foreach((array)array_rand($skills, mt_rand(1, 3)) as $key ) { $userSkills[] = $skills[$key]; } $users['user_'.$i] = (new User())  // Zufälliger Name unter Vermeidung von Dopplungen ->setName(array_pop($names))  // Eindeutige E-Mail-Adresse ->setEmail(sprintf('user-%d@example.org', $i))  // Zufälliger Straßenname ->setStreet('Musterstraße '.mt_rand(1, 199))  // Zufällige 5-stellige PLZ mit Zerofill ->setZip(sprintf('%05d', mt_rand(0, 99999)))  // Zufälliger hexadezimaler Farbcode ->setFavoriteColor(sprintf('#%06X', mt_rand(0, 0xFFFFFF)))  // Zufällige PHP-Zeitzone ->setTimezone($timezones[array_rand($timezones)])  // Blindtext mit 35-prozentiger Wahrscheinlichkeit ->setBio(mt_rand(0, 100) <= 35 ? $loremIpsum : null)  // Mehrere Zufallselemente aus einem Array ->setSkills($userSkills)  // Letzter Login (in der Vergangenheit) ->setLastLogin(new \DateTime('@'.(time()-mt_rand(0, 3600*72)))); } // User persistieren $objectManager->persist($users);

Mit der Vielfalt an weiteren Datentypen wie Geopunkte, Länder, Locales, User-Agent-Strings, IP- und Mac-Adressen, Kreditkartendaten oder Zufallsbilder ließe sich die Liste an nötiger Logik zur Erzeugung von Fakedaten beliebig weiterführen. Dabei finden sich auch immer wiederkehrende Muster für Logik zur zufälligen, eindeutigen oder optionalen Auswahl von einem oder mehreren Elementen aus einer Wertemenge.

Alle Maßnahmen führen schnell zu unübersichtlichem, redundantem und schwer wartbarem Code. Trotz einigem Aufwand wirken viele Daten wenig real, etwa wenn sie sich nur durch einen Schleifenindex oder eine Zufallszahl unterscheiden. Und möchte man in einer mehrsprachigen Applikation sprachtypische Werte für Namen, Postleitzahlen, Telefonnummern usw. verwenden, multipliziert sich der Aufwand weiter. Der geneigte Entwickler wird beginnen, Codeteile zu abstrahieren, in eine kleine Bibliothek auszulagern und wiederzuverwenden. Spätestens wenn diese Bibliothek dann projektübergreifend im Einsatz ist, entsteht Verbesserungs- und Pflegeaufwand.

Unter der Annahme, dass für Fixtures meistens wenig Raum im Projektzeitplan vorgesehen ist, lautet die gute Nachricht: Mit Faker gibt es bereits eine Bibliothek, die alle beschriebenen Anforderungen erfüllt. Sie wird am besten als Composerpaket in eine Anwendung integriert: $ composer require fzaninotto/Faker. Der Composer-Autoloader ermöglicht im Anschluss die einfache Verwendung der Faker-Klassen: require_once 'vendor/autoload.php';.

Die Methoden zur Erzeugung von Testdaten nennt Faker Formatter und kategorisiert sie in Provider-Klassen. Der Person-Provider stellt alle personenbezogenen Formatter für Anrede, Vorname, Nachname etc. zur Verfügung, der Internet-Provider kumuliert alle internetbezogenen Formatter, etwa für E-Mail, URL oder IP. Die eigentliche Arbeit mit Testdaten erfolgt über ein Generator-Objekt, das beliebig viele Provider bündelt. Der Einfachheit halber stellt Faker eine Factory für ein Generator-Objekt bereit, das bereits alle vorhandenen Provider von Faker einbindet:

$faker = Faker\Factory::create();

Fragt man das $faker-Generator-Objekt na...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang