Einleitung: Warum Ihre Tests lügen und Sie ihnen glauben
\n\nStellen Sie sich vor: Sie haben 50 Unit-Tests geschrieben, alle if-else-Zweige abgedeckt, durch CI gejagt – alles grün. Eine Woche später stürzt die Produktion mit dem Fehler "Division by zero" ab. Wie? Die Tests liefen doch! Kommt Ihnen das bekannt vor?
Das Problem klassischer Unit-Tests (beispielhafter Tests mit konkreten Zahlen) ist, dass sie das überprüfen, was Sie bereits wissen. Sie wählen Argumente aus, von denen Sie glauben, dass sie zu einem Fehler führen. Aber der Verstand eines Programmierers ist vorhersehbar: Wir schreiben alle testDivideByZero und vergessen testDivideByNegativeZero oder testDivideByVeryLargeFloat.
Hier kommt das Property-basierte Testen in PHP ins Spiel – eine Methode, die nicht konkrete Szenarien prüft, sondern Eigenschaften Ihres Codes für Tausende von zufälligen Eingabedaten beweist. Heute werden wir durchgehen, wie Sie diesen Ansatz in Ihr PHP-Projekt integrieren, unter Verwendung von Eris und PHPUnit. Sie werden erfahren, wie Sie Fehler finden, die normale Tests übersehen, und warum das keine Magie, sondern Ingenieurskunst ist.
\n\nWas ist Property-basiertes Testen? (und warum es kein Zufall ist)
\n\nEin normaler Unit-Test sieht so aus: assert(2 + 2 === 4). Sie überprüfen eine einzige konkrete Behauptung. Property-basiertes Testen (PBT) funktioniert anders: Sie formulieren eine Eigenschaft – eine Behauptung, die für alle korrekten Eingabedaten wahr sein muss.
Beispiele für Eigenschaften einer Additionsfunktion:
\n- \n
- Kommutativität:
a + b === b + afür alle Zahlen. \n - Idempotenz: Die wiederholte Anwendung der Funktion ändert das Ergebnis nicht. \n
- Invarianz: Das Ergebnis ist immer positiv (wenn dies eine Eigenschaft ist). \n
PBT generiert hunderte von zufälligen Werten (einschließlich Grenzfällen, Null, Negativwerten, NaN) und prüft, ob die Eigenschaft verletzt wird. Wenn ein Gegenbeispiel gefunden wird, schlägt der Test mit den konkreten Eingabedaten fehl, die den Fehler verursacht haben. Sie erhalten nicht nur "Test fehlgeschlagen", sondern einen reproduzierbaren Fehler.
\n\nIn PHP ist diese Technik aufgrund der dynamischen Typisierung besonders nützlich: Sie wissen nie, welcher Typ tatsächlich in einer Funktion ankommt.
\n\nWerkzeuge für Property-basiertes Testen in PHP
\n\nDas wichtigste Werkzeug ist die Bibliothek Eris (github.com/giorgiosironi/eris). Sie integriert sich in PHPUnit und bietet Generatoren für zufällige Daten. Eine Alternative sind die integrierten Funktionen von PHPUnit 10+ (die aber noch etwas roh sind). Wir werden Eris verwenden, da es ein ausgereiftes und dokumentiertes Werkzeug ist.
\n\nInstallation via Composer:
\ncomposer require --dev giorgiosironi/eris\n\nEbenfalls nützlich ist Faker zur Generierung komplexer Strukturen (E-Mail, Namen, Adressen).
\n\ncomposer require --dev fakerphp/faker\n\nUnseren ersten Property-basierten Test schreiben: Sortierung kaputt machen
\n\nAngenommen, wir haben eine Funktion sortArray(), die Quicksort verwendet. Schreiben wir eine Eigenschaft: "Das Array darf nach dem Sortieren kein Element enthalten, das kleiner ist als das vorherige."
use Eris\\TestTrait;\nuse Eris\\Generator\\IntegerGenerator;\nuse PHPUnit\\Framework\\TestCase;\n\nclass SortTest extends TestCase\n{\n use TestTrait;\n\n public function testSortedArrayProperty(): void\n {\n $this->forAll(\n Generator\\seq(Generator\\int())\n )->then(function (array $input) {\n $sorted = sortArray($input);\n for ($i = 0; $i < count($sorted) - 1; $i++) {\n $this->assertLessThanOrEqual(\n $sorted[$i + 1],\n $sorted[$i],\n "Array ist nicht sortiert: " . json_encode($sorted)\n );\n }\n });\n }\n}\n\n\nAusführung