Ordentliche und schnelle unit tests in WordPress

In meinem Hacktoberfest-Projekt fehlten noch ein paar Unit Tests, doch leider gibt es dazu kein Framework. Das was rund um PHPUnit für WordPress angeboten wird sind eher Integrationstests. Also habe ich etwas mehr recherchiert und dank zwei Konstanten in WordPress habe ich nun schnelle „Unit Tests“.Read the more up to date and famous one afterwards!
Capabilities für einzelne Terms in WP 4.7
Open in new tab for later

Wie testet ihr den Code? Oder habe ich ein Framework übersehen?
Wenn dem so ist, dann freue ich mich natürlich über ein Kommentar.

Nirgends ordentliche Unit Tests

Okay, das ist hart, alle Entwickler über einen Kamm geschert und unfair. Leider fühlt es sich für mich häufig so an, wenn es um Unit Tests geht. Vieles ist einfach am Ziel vorbei, doch heute habe ich eine Lösung für mich gefunden!

Geschätzt jeder zweite Entwickler macht leider das mit den Unit Tests falsch. Es gibt die, die Integrationstests schlicht als UnitTest betiteln und solche, die ordentliche UnitTests machen aber sich die finger beim Mocken wund schreiben. Ich möchte die Unterschiede der beiden Sachen kurz erklären.

Unit tests testen eine einzelne Einheit

Unit tests sind zu einem sehr modularen/atomaren Test für einzelne Klassen oder Funktionen geworden. Überschreitet der Entwickler dieses Limit, so ist es kein Unit Test mehr. Dazu gleich bei den Integrationstests mehr.

Solche Tests sollten blitzschnell durchgeführt sein. Einzelne Tests (diese „testFooKannBar()“ Funktionen) sollten in weniger als 0,1 Sekunde fertig sein. Am besten noch ist das gesamte Testing aller Units innerhalb von 5 Sekunden erledigt. Üblicherweise werden diese Unit Tests vor jedem Commit oder jedem Push einmal durchgeführt und da soll natürlich keine rießen Wartezeit die Produktivität hindern.

Sehen wir uns eine Funktion mal im Detail an:

function get_more_recent_post_rand( $date ) {
  $posts = get_posts( [
    'date_query' => [ 'after' => $date ]
  ] );
  
  return array_rand( $posts );
}

Die zu testende Einheit ist hier die „get_more_recent_post_rand“-Funktion, welche auf eine andere Einheit mit dem Namen „WordPress“ zurückgreift. Die Funktion „get_posts“ kommt aus dem Core und sollte an dieser Stelle gemockt werden, ansonsten handelt es sich nicht mehr um einen abgeschlossenen Unit Test. PHPUnit und Codeception können dabei behilflich sein.

Doch meist werden in Tests solche Stellen nicht mit einem Mock versehen. Oft sehe ich wir das gesamte WordPress geladen wird und dann wiederhole ich: Kommt eine Komponente hinzu, dann handelt es sich um einen Integrationstest.

Integration tests bringen andere Komponenten mit

Dies trage ich lange im Kopf mit mir herum und ich habe mich sogar rückversichert, woraus dieser Post hier entstand. Es ergab sich in meinem Theme das Problem, dass ich fast alle Funktionen von WordPress mocken musste. Das fühlte sich falsch an, also habe ich nachgehakt.

Eine kurze Frage an die Freunde der PHPUnit-Community und schnell kam eine Antwort. Jakub Zalas, Entwickler bei Sensio Labs für Symfony und PHPSpec, bestätigte die Denke:

if tests load a whole framework they’re not unit tests 😀

Das ist eine schöne Zusammenfassung wo UnitTests aufhören und Integratinostests anfangen. An sich freut es mich zu sehen, dass so viele Entwickler Tests von Plugins und themes machen. Bitte nennt es Integrationstests, wenn dabei WordPress komplett geladen wird.

Solche Integrationstests sind komplexer, weil Sie Software von Drittanbietern mit rein holen. Das wäre ein weiteres Framework, ein Netzwerk-Adapter, eine Teekanne oder irgendetwas anderes zusätzliches in der Testumgebung sein. Sobald mehrere Systeme eingebettet werden benötigen die Tests auch mehr Zeit. Die gehen dann mehrere Sekunden, Minuten und auch schon welche über mehrere Tage habe ich gesehen.

Die Welt dahinter hat noch mehr Testklassen

Neben diesen zwei Testklassen gibt es noch die Regressionstests, welche ebenfalls häufig durchgeführt werden. Dabei werden schlichtweg alle Tests laufen gelassen, welche bisher geschrieben worden sind. Damit soll sichergegangen werden, dass eine Änderung oder neues Feature keines der bestehenden kaputt macht.

Der Akzeptanztest wird direkt von den Stakeholdern persönlich durchgeführt. Ich für meinen Teil schalte per Behat noch eine Stufe dazwischen, bevor die Stakeholder alle Arbeiten zur Abnahme bekommen. Ein leicht modifiziertes Behat lässt die Tests etwas langsamer laufen und bezieht die Zeit zum Element suchen, Maus bewegen, Klicken und Eingabe per Tastatur mit ein. Denn gute Bedienbarkeit kommt nicht nur von funktionierenden Webseiten sondern auch kurzen schnellen Wegen zum Ziel.

Die Lösung: Ein schmaler Layer dazwischen

Dies ist ein Blog über den WordPress Core und keine Meta-Diskussionen darüber was Tests sind. Daher darf eine Lösung für WordPress nicht fehlen.

Das jeder jetzt das Mocken anfängt, um echte Unittests zu erreichen wäre etwas viel Aufwand – sehr viel sogar, ich habe es probiert. Ein Vorschlag aus der PHPUnit-Community war, einen schmalen Layer zwischen dem Plugin und dem Framework zu schieben. Diesen habe ich tatsächlich im Core gefunden. Ein sehr einfacher Schritt, für schnellere Tests und großem Hang zum Unit Test.

WordPress abschalten

Zumindest einen Großteil davon. Die „SHORTINIT“ konstante lädt nur einen Teil von WordPress und nicht mehr das große Ganze. Die zusätzliche Komponente WordPress ist damit nicht mehr so groß und es lassen sich viel schöner einzelne Bereiche als Unit testen. Eine Zeile für das Bootstrapping genügt:

define( 'SHORTINIT', true );

Schon brauchte das Laden von WordPress nur noch 0,01 Sekunden in meinemn Tests. Wenige schlagen danach fehl, was durch ein paar require_once oder vernünftiges Mocken schnell behoben war. Mockery ist ein sehr Mchtiges Tool für Test-Doubles aber auch BrainMonkey ist im WordPress-Kontext sehr interessant.

Oder einfach die Lichter ausschalten

Für schnellere Integrationstest von Plugins kann das Theme ausgeschaltet werden, um den Layer zwischen der Eigenentwicklung und Framework flach zu halten. Auch hier wieder eine einfache Konstante beim Bootstrapping setzen:

// Hey, who turned out the lights?
define( 'WP_USE_THEMES', false );

Jetzt benötigt WordPress nur noch 0,25 Sekunden zum laden, was durchaus in Ordnung ist. Das meißte von WordPress nebst allen plugins ist verfgbar. Sehr schön, um sein eigenes Plugin oder theme gegen andere zu testen.

Ab in die PHPUnit konfig damit

Die Konstanten sollten schon beim bootstrap Prozess vorhanden sein, noch bevor WordPress geladen wird. Dies kann so aussehen:

//define( 'SHORTINIT', true );
define( 'WP_USE_THEMES', false );

$dir = __DIR__;

while ( dirname( $dir ) != $dir && $dir = dirname( $dir ) ) {
   $wp_load = $dir . DIRECTORY_SEPARATOR . 'wp-load.php';

   if ( file_exists( $wp_load ) ) {
      require_once $wp_load;
      break;
   }
}

Es gibt allerdings auch die Option es in eine eigene phpunti.xml Konfiguration zu schreiben:

<php>
    <const name="SHORTINIT" value="true" />
</php>

Das ist etwas magischer und nerdiger. Dieser Weg ist mir sogar lieber, denn damit sind mehrere PHPUnit Konfigurationen möglich, um eine für Quasi-UnitTests zu haben (SHORTINIT), eine für Integrationstests (WP_USE_THEMES) und was sonst noch anfällt.

Fazit

Ich werde alt. Nach 15 Jahren programmieren ist es interessant wie viel einem bekannt ist und wie viel es noch zu lernen gibt. Manche Buzzwords werden leider anders benutzt als definiert und ich hoffe mit diesem Post etwas aufklärung betrieben zu haben. Vielleicht vertue ich mich auch total nd jemand schreibt in einem Kommentar warum. Auf jeden Fall profitieren nun viele der tausenden Leser von einem schnelleren Testing und nennen es künftig Integrationstest.

Wie stehst Du zum testing? Ab welcher Projektgröße lohnt es sich? Welche Art von Tests machst Du am liebsten? Es kann mir gern jeder einen Kommentar da lassen, ich habe echt gerade Lust über Vor- und Nachteile zu diskutieren.

Btw: Wer mehr über das theme wissen möchte, für welches diese Tests gebaut werden, dann am Wochenende nochmal vorbei schauen. Ein Post zum Hacktoberfest wird folgen.

2 Gedanken zu „Ordentliche und schnelle unit tests in WordPress“

  1. Sehr interessanter Post, man liest ja eher selten was über (Unit) Testing im WordPress-Umfeld. Danke dafür.
    Noch ein paar Grammar Nazi Anmerkungen: „meißt“ mit scharf ß gibts nicht und hier muss ein „dass“ : „Es ergab sich in meinem Theme das Problem, das“.
    Sorry, bin da pingelig 😉

    1. Danke Andreas! 🙂 Grammar-Nazis sind besonders willkommen bei mir. Ich mag immer alles so perfekt und richtig haben, aber so Fehler wie „meißt“ verfolgen mich seit Jahren und geschehen immer wieder.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *