~/wordpress-plugins/phpunit/

PHPUnit für WordPress-Plugins: Code automatisch absichern

Automatische Tests mit PHPUnit: wie Sie das Testgerüst anlegen, reine Logik von WordPress-Integrationen trennen, einen ersten Filter absichern und Tests regelmäßig laufen lassen.

Ein Plugin besteht schnell aus vielen Teilen: Es verarbeitet Einstellungen, hängt sich an Hooks, verändert Inhalte oder stellt Funktionen für andere Erweiterungen bereit. Schon eine kleine Änderung kann an ganz anderer Stelle etwas kaputt machen. Automatische Tests mit PHPUnit fangen solche Fehler ab, bevor das Plugin bei Nutzern landet.

Es geht dabei nicht darum, jede Codezeile abzudecken. Entscheidend ist, die wichtigen Regeln des Plugins nachprüfbar festzuhalten: Wird ein ungültiger Wert abgewiesen? Bleibt eine Funktion bei leeren Eingaben stabil? Wird ein Hook nur unter den vorgesehenen Bedingungen registriert? Solche Tests geben Sicherheit, wenn Sie den Code später erweitern oder umbauen.

Unit-Tests und Integrationstests

PHPUnit kann einzelne Funktionen völlig unabhängig von WordPress prüfen und, mit dem WordPress-Testframework, auch das Zusammenspiel mit WordPress. Für Plugins ist diese Trennung wichtig.

Reine Logik isoliert prüfen

Eine Funktion, die Eingaben umwandelt, validiert oder einen Wert berechnet, lässt sich oft ganz ohne WordPress ausführen. Solche Unit-Tests sind schnell und haben kaum Abhängigkeiten:

self::assertSame( 8, meinplugin_summe( 3, 5 ) );

Je klarer eine Funktion Eingaben entgegennimmt und ein Ergebnis zurückgibt, desto leichter ist sie isoliert zu prüfen. Greift sie dagegen direkt auf globale Variablen, die Datenbank oder viele WordPress-Funktionen zu, wird es schwierig. Dann lohnt es sich, die eigentliche Logik in eine eigene Funktion oder Klasse auszulagern und die WordPress-Anbindung davon zu trennen.

Ein einzelner Vergleich ist nur aussagekräftig, wenn klar ist, welche Regel er abbildet. Bei einer Validierung gehören neben einem gültigen Wert auch leere, unerwartete und falsch formatierte Eingaben dazu.

Das Zusammenspiel mit WordPress prüfen

Plugin-Code nutzt fast immer Hooks, Beiträge, Optionen oder Benutzer. Für solche Fälle gibt es das WordPress-Testframework mit der Basisklasse WP_UnitTestCase. Trotz des Namens sind das Integrationstests: Sie laden WordPress, arbeiten mit einer eigenen Testdatenbank und führen Ihren Code zusammen mit echten WordPress-Funktionen aus.

Integrationstests beantworten andere Fragen, etwa ob ein Filter korrekt registriert ist oder ob eine Funktion mit den Daten, die WordPress liefert, richtig umgeht. Dafür sind sie langsamer und aufwendiger, weil WordPress geladen und der nötige Zustand vorbereitet werden muss.

Wählen Sie deshalb immer die kleinste passende Ebene. Eine einfache Berechnung braucht keine komplette WordPress-Instanz. Umgekehrt beweist ein isolierter Test nichts, wenn gerade das Zusammenspiel mit einem Hook abgesichert werden soll.

Das Testgerüst mit WP-CLI anlegen

Am schnellsten kommen Sie mit WP-CLI zu einer funktionierenden Grundstruktur. Der Befehl erzeugt die nötigen Dateien für ein Plugin mit dem angegebenen Slug:

wp scaffold plugin-tests mein-plugin

Laut WP-CLI-Dokumentation entstehen dabei unter anderem:

  • phpunit.xml.dist, die Konfiguration für PHPUnit,
  • bin/install-wp-tests.sh, das die WordPress-Testsuite und eine Testdatenbank einrichtet,
  • tests/bootstrap.php, die Ihr Plugin beim Testlauf aktiviert,
  • tests/test-sample.php als Beispieltest,
  • eine Konfigurationsdatei für einen CI-Dienst, die Sie mit der Option --ci auswählen, etwa --ci=github.

Die Konfiguration legt fest, wo die Testdateien liegen und welche Datei vor dem Start geladen wird. Der Bootstrap bereitet den Kontext vor: Für reine Unit-Tests genügt eine schlanke PHP-Datei, für Integrationstests bindet er das WordPress-Testframework ein. Die WordPress-Instanz und die Datenbank dafür kann eine lokale Umgebung bereitstellen, wie sie der Artikel Lokale Entwicklungsumgebung mit wp-env einrichten beschreibt.

PHPUnit selbst verwalten Sie am besten als Entwicklungsabhängigkeit über Composer, getrennt von den Abhängigkeiten, die das Plugin im Betrieb braucht. Welche PHPUnit-Version passt, hängt von Ihrer PHP-Version und den Anforderungen des WordPress-Testframeworks ab. Prüfen Sie die Kompatibilität, bevor Sie eine Version festlegen, und dokumentieren Sie alle Voraussetzungen im Projekt, damit die Tests nicht unbemerkt von Besonderheiten Ihres Rechners abhängen.

Der erste Test für einen Filter

Als Beispiel dient der Filter aus dem Artikel WordPress-Hooks: Actions und Filter erklärt: Die Funktion meinplugin_titel_markieren hängt an the_title und ergänzt bei Beiträgen mit dem Schlagwort „aktualisiert“ einen Hinweis im Titel. Zwei Tests sichern das ab:

class Test_Titel_Filter extends WP_UnitTestCase {

    public function test_filter_ist_registriert() {
        $this->assertNotFalse(
            has_filter( 'the_title', 'meinplugin_titel_markieren' )
        );
    }

    public function test_titel_wird_markiert() {
        $post_id = self::factory()->post->create(
            array( 'post_title' => 'Hallo' )
        );
        wp_set_post_tags( $post_id, 'aktualisiert' );

        $this->assertSame( 'Hallo (aktualisiert)', get_the_title( $post_id ) );
    }
}

Der erste Test prüft nur, ob die Funktion am Hook hängt. Der zweite legt über die Factory des Testframeworks einen Beitrag an, vergibt das Schlagwort und vergleicht den Titel, den WordPress tatsächlich ausgibt. Das Testframework setzt die Datenbank nach jedem Test zurück, sodass der angelegte Beitrag keinen anderen Test beeinflusst. Ergänzen Sie sinnvollerweise einen dritten Test für einen Beitrag ohne Schlagwort, dessen Titel unverändert bleiben muss.

Aussagekräftige Tests schreiben

Ein guter Test macht genau eine Erwartung sichtbar. Statt viele unabhängige Prüfungen in einem langen Ablauf zu bündeln, deckt jeder Test eine überschaubare Regel ab. Schlägt er fehl, sehen Sie sofort, welche Annahme nicht mehr stimmt.

  • Prüfen Sie Verhalten und Ergebnisse, nicht den internen Aufbau des Codes.
  • Wählen Sie Testnamen, die die geprüfte Regel beschreiben.
  • Testen Sie gezielt auch leere oder ungültige Werte, wenn sie vorkommen können.
  • Bereiten Sie nur den Zustand vor, den der jeweilige Test braucht.
  • Räumen Sie angelegte Daten wieder auf, falls das Framework das nicht übernimmt.
  • Vermeiden Sie Abhängigkeiten zwischen einzelnen Tests.

Bei WordPress spielt der Zustand eine besondere Rolle. Ein Test, der Optionen setzt oder Beiträge anlegt, darf nicht auf Daten bauen, die ein anderer Test hinterlassen hat. Nur dann sind die Ergebnisse wiederholbar, und die Reihenfolge der Ausführung spielt keine Rolle.

Externe Dienste haben in einem automatischen Testlauf nichts zu suchen. Spricht Ihr Plugin eine entfernte Schnittstelle an, ersetzen Sie deren Antwort beim Testen durch eine kontrollierte Attrappe. So hängt das Ergebnis nicht von Netzwerk, Zugangsdaten oder einem fremden Dienst ab.

Tests regelmäßig ausführen

Tests nützen nur, wenn sie auch laufen. Während der Entwicklung starten Sie PHPUnit über die Kommandozeile im Plugin-Ordner. Ist es per Composer installiert, lautet der Aufruf meist vendor/bin/phpunit, PHPUnit liest dann die phpunit.xml.dist ein.

Zusätzlich sollte ein automatischer Ablauf die Tests bei jeder Änderung im Repository starten, also in einer CI wie GitHub Actions oder GitLab CI. Die passende Konfigurationsdatei erzeugt wp scaffold plugin-tests auf Wunsch gleich mit. So fällt ein Fehler auf, bevor eine neue Version veröffentlicht wird. Wie sich Erweiterungen zusätzlich im Browser automatisch prüfen lassen, zeigt der Artikel Browser-Erweiterungen testen mit Playwright.

Einen fehlgeschlagenen Lauf sollten Sie nie einfach übergehen. Klären Sie zuerst, ob sich das Verhalten des Plugins geändert hat oder ob die Erwartung des Tests veraltet ist. Ein Test, der regelmäßig ignoriert wird, schützt den Code nicht mehr.

Häufige Stolperstellen

Ein verbreiteter Fehler ist, zu viele Details der Umsetzung festzuschreiben. Prüft ein Test interne Methoden oder die genaue Reihenfolge unwichtiger Aufrufe, zieht jede sinnvolle Codeänderung unnötig viele Anpassungen nach sich. Konzentrieren Sie sich auf das Verhalten, das für Nutzer oder andere Teile des Plugins wirklich zählt.

Auch viele oberflächliche Tests helfen wenig. Ein paar aussagekräftige Fälle für zentrale Regeln sind wertvoller als eine lange Liste von Vergleichen, die keinen echten Fehlerfall abdecken. Beginnen Sie mit Funktionen, deren Ausfall spürbare Folgen hätte oder die sich häufig ändern.

Und schließlich: Ein Unit-Test, der WordPress nur nachahmt, beweist nicht, dass die Integration funktioniert. Dafür ist ein Test mit dem WordPress-Testframework da. Umgekehrt sollte einfache Logik nicht unnötig von einer kompletten WordPress-Laufzeit abhängen.

Fazit

PHPUnit macht die wichtigen Regeln eines WordPress-Plugins wiederholbar überprüfbar. Legen Sie das Gerüst mit WP-CLI an, trennen Sie reine Logik von WordPress-Integrationen, und beginnen Sie mit den Funktionen, auf die es am meisten ankommt. Laufen die Tests bei jeder Änderung automatisch mit, werden sie zu einem verlässlichen Teil der Entwicklung. Weitere Werkzeuge und Grundlagen finden Sie in der Übersicht WordPress-Plugins entwickeln, zeitgesteuerte Abläufe behandelt der Artikel WP-Cron richtig einsetzen.

faq

Fragen und Antworten

Braucht jede Funktion eines Plugins einen eigenen Test?
Nein. Beginnen Sie mit Funktionen, die zentrale Regeln abbilden, komplex sind oder häufig geändert werden. Kleine, leicht überschaubare Hilfsfunktionen brauchen nicht automatisch einen Test, wenn er kein echtes Risiko abdeckt.
Was ist bei älteren Plugin-Projekten zu beachten?
Klären Sie zuerst, welche PHP- und WordPress-Versionen das Plugin unterstützen muss und welche PHPUnit-Version dazu passt. Bei bestehendem Code ist es meist sinnvoll, Tests schrittweise für geänderte oder besonders wichtige Bereiche aufzubauen, statt das ganze Plugin auf einmal umzubauen.
Warum schlagen Tests manchmal nur gelegentlich fehl?
Tests sollten unabhängig von Reihenfolge, Uhrzeit, Netzwerk und fremden Diensten laufen. Schwankende Ergebnisse deuten fast immer auf gemeinsam genutzten Zustand oder nicht kontrollierte Abhängigkeiten hin. Isolieren Sie den betroffenen Test und machen Sie seine Voraussetzungen ausdrücklich.