Einzeltest
Wenn Sie ein Plugin entwickeln, testen Sie es am besten, ohne die WordPress-Umgebung zu laden.
Wenn Sie Code schreiben, der ohne WordPress einfach getestet werden kann, wird Ihr Code besser .
Jede Komponente, die Unit-getestet wird, sollte isoliert getestet werden : Wenn Sie eine Klasse testen, müssen Sie nur diese bestimmte Klasse testen, vorausgesetzt, der gesamte andere Code funktioniert einwandfrei.
Aus diesem Grund werden Unit-Tests als "Unit" bezeichnet.
Als zusätzlicher Vorteil wird Ihr Test ohne Laden des Kerns viel schneller ausgeführt.
Vermeiden Sie Haken im Konstruktor
Ein Tipp, den ich Ihnen geben kann, ist, dass Sie keine Haken in Konstruktoren stecken. Dies ist eines der Dinge, die Ihren Code für sich allein testbar machen.
Sehen wir uns den Testcode in OP an:
class CustomPostTypes extends WP_UnitTestCase {
function test_custom_post_type_creation() {
$this->assertTrue( post_type_exists( 'foo' ) );
}
}
Nehmen wir an, dieser Test schlägt fehl . Wer ist der Täter ?
- Wurde der Haken überhaupt nicht oder nicht richtig hinzugefügt?
- Die Methode, die den Beitragstyp registriert, wurde überhaupt nicht oder mit falschen Argumenten aufgerufen.
- gibt es einen Fehler in WordPress?
Wie kann es verbessert werden?
Angenommen, Ihr Klassencode lautet:
class RegisterCustomPostType {
function init() {
add_action( 'init', array( $this, 'register_post_type' ) );
}
public function register_post_type() {
register_post_type( 'foo' );
}
}
(Hinweis: Ich werde für den Rest der Antwort auf diese Version der Klasse verweisen.)
Die Art und Weise, wie ich diese Klasse geschrieben habe, ermöglicht es Ihnen, Instanzen der Klasse zu erstellen, ohne sie aufzurufen add_action
.
In der obigen Klasse gibt es zwei Dinge zu testen:
- Die Methode ruft
init
tatsächlich die add_action
Übergabe der richtigen Argumente auf
- Die Methode ruft
register_post_type
tatsächlich die register_post_type
Funktion auf
Ich habe nicht gesagt , dass Sie , wenn Post - Typen überprüfen vorhanden ist : wenn Sie die richtige Aktion hinzufügen und wenn Sie anrufen register_post_type
, der benutzerdefinierte Post - Typ muss vorhanden sein: wenn es nicht existiert es ein Wordpress - Problem.
Denken Sie daran: Wenn Sie Ihr Plugin testen, müssen Sie Ihren Code testen , nicht WordPress-Code. In Ihren Tests müssen Sie davon ausgehen, dass WordPress (genau wie jede andere externe Bibliothek, die Sie verwenden) gut funktioniert. Das ist die Bedeutung von Unit Test.
Aber ... in der Praxis?
Wenn WordPress nicht geladen ist und Sie versuchen, die oben genannten Klassenmethoden aufzurufen, wird ein schwerwiegender Fehler angezeigt, sodass Sie die Funktionen verspotten müssen.
Die "manuelle" Methode
Sicher können Sie Ihre Spottbibliothek schreiben oder jede Methode "manuell" verspotten. Es ist möglich. Ich erkläre Ihnen, wie das geht, aber dann zeige ich Ihnen eine einfachere Methode.
Wenn WordPress nicht geladen wird, während Tests ausgeführt werden, können Sie seine Funktionen neu definieren, z . B. add_action
oder register_post_type
.
Nehmen wir an, Sie haben eine Datei aus Ihrer Bootstrap-Datei geladen, in der Folgendes enthalten ist:
function add_action() {
global $counter;
if ( ! isset($counter['add_action']) ) {
$counter['add_action'] = array();
}
$counter['add_action'][] = func_get_args();
}
function register_post_type() {
global $counter;
if ( ! isset($counter['register_post_type']) ) {
$counter['register_post_type'] = array();
}
$counter['register_post_type'][] = func_get_args();
}
Ich habe die Funktionen umgeschrieben, um einfach jedes Mal, wenn sie aufgerufen werden, ein Element zu einem globalen Array hinzuzufügen.
Jetzt sollten Sie (falls Sie noch keine haben) eine eigene Basis-Testfallklassenerweiterung erstellen PHPUnit_Framework_TestCase
, mit der Sie Ihre Tests einfach konfigurieren können.
Es kann so etwas sein wie:
class Custom_TestCase extends \PHPUnit_Framework_TestCase {
public function setUp() {
$GLOBALS['counter'] = array();
}
}
Auf diese Weise wird vor jedem Test der globale Zähler zurückgesetzt.
Und jetzt dein Testcode (ich beziehe mich auf die neu geschriebene Klasse, die ich oben gepostet habe):
class CustomPostTypes extends Custom_TestCase {
function test_init() {
global $counter;
$r = new RegisterCustomPostType;
$r->init();
$this->assertSame(
$counter['add_action'][0],
array( 'init', array( $r, 'register_post_type' ) )
);
}
function test_register_post_type() {
global $counter;
$r = new RegisterCustomPostType;
$r->register_post_type();
$this->assertSame( $counter['register_post_type'][0], array( 'foo' ) );
}
}
Sie sollten beachten:
- Ich konnte die beiden Methoden separat aufrufen und WordPress ist überhaupt nicht geladen. Auf diese Weise weiß ich genau, wer der Schuldige ist , wenn ein Test fehlschlägt .
- Wie gesagt, hier teste ich, dass die Klassen WP-Funktionen mit erwarteten Argumenten aufrufen. Es muss nicht geprüft werden, ob CPT wirklich vorhanden ist. Wenn Sie die Existenz von CPT testen, testen Sie das WordPress-Verhalten, nicht Ihr Plugin-Verhalten ...
Schön .. aber es ist ein PITA!
Ja, wenn Sie alle WordPress-Funktionen manuell verspotten müssen, ist das wirklich ein Schmerz. Ein allgemeiner Rat, den ich geben kann, ist, so wenig WP-Funktionen wie möglich zu verwenden: Sie müssen WordPress nicht umschreiben , sondern abstrakte WP-Funktionen, die Sie in benutzerdefinierten Klassen verwenden, damit sie verspottet und einfach getestet werden können.
Zum Beispiel können Sie für das obige Beispiel eine Klasse schreiben, die Post-Typen registriert register_post_type
und mit den angegebenen Argumenten 'init' aufruft . Mit dieser Abstraktion müssen Sie diese Klasse noch testen, aber an anderen Stellen Ihres Codes, an denen Beitragstypen registriert sind, können Sie diese Klasse verwenden und sie in Tests verspotten (vorausgesetzt, sie funktioniert).
Das Tolle ist, wenn Sie eine Klasse schreiben, die die CPT-Registrierung abstrahiert, können Sie ein separates Repository dafür erstellen und dank moderner Tools wie Composer in alle Projekte einbetten, in denen Sie es benötigen: einmal testen, überall verwenden . Und wenn Sie jemals einen Fehler darin finden, können Sie ihn an einer Stelle beheben, und mit einer einfachen Methode werden composer update
alle Projekte, in denen er verwendet wird, ebenfalls behoben.
Zum zweiten Mal: Code zu schreiben, der isoliert getestet werden kann, bedeutet, besseren Code zu schreiben.
Aber früher oder später muss ich irgendwo WP-Funktionen verwenden ...
Na sicher. Du solltest niemals parallel zum Kern agieren , es macht keinen Sinn. Sie können Klassen schreiben, die WP-Funktionen umschließen, aber diese Klassen müssen ebenfalls getestet werden. Die oben beschriebene "manuelle" Methode kann für sehr einfache Aufgaben verwendet werden, aber wenn eine Klasse viele WP-Funktionen enthält, kann dies schmerzhaft sein.
Zum Glück gibt es dort gute Leute, die gute Dinge schreiben. 10up , eine der größten WP-Agenturen, unterhält eine großartige Bibliothek für Leute, die Plugins richtig testen möchten. Es ist WP_Mock
.
Hiermit können Sie WP-Funktionen und Hooks nachahmen . Angenommen, Sie haben in Ihren Tests (siehe Repo-Readme) denselben Test geladen, den ich oben geschrieben habe:
class CustomPostTypes extends Custom_TestCase {
function test_init() {
$r = new RegisterCustomPostType;
// tests that the action was added with given arguments
\WP_Mock::expectActionAdded( 'init', array( $r, 'register_post_type' ) );
$r->init();
}
function test_register_post_type() {
// tests that the function was called with given arguments and run once
\WP_Mock::wpFunction( 'register_post_type', array(
'times' => 1,
'args' => array( 'foo' ),
) );
$r = new RegisterCustomPostType;
$r->register_post_type();
}
}
Einfach, nicht wahr? Diese Antwort ist kein Tutorial für WP_Mock
, lesen Sie also die Repo-Readme für weitere Informationen, aber das obige Beispiel sollte ziemlich klar sein, denke ich.
Darüber hinaus müssen Sie keine verspotteten add_action
oder register_post_type
selbst erstellten oder globalen Variablen verwalten.
Und WP-Klassen?
WP hat auch einige Klassen, und wenn WordPress beim Ausführen von Tests nicht geladen ist, müssen Sie sie verspotten.
Das ist viel einfacher als das Verspotten von Funktionen. PHPUnit verfügt über ein eingebettetes System zum Verspotten von Objekten. Hier möchte ich Ihnen jedoch Mockery vorschlagen . Es ist eine sehr leistungsfähige Bibliothek und sehr einfach zu bedienen. Außerdem ist es eine Abhängigkeit von WP_Mock
. Wenn Sie es haben, haben Sie auch Spott.
Aber was ist mit WP_UnitTestCase
?
Die WordPress-Testsuite wurde erstellt, um den WordPress- Kern zu testen. Wenn Sie einen Beitrag zum Kern leisten möchten, ist dies von entscheidender Bedeutung. Wenn Sie sie jedoch nur für Plugins verwenden, werden Sie nicht isoliert getestet.
Werfen Sie einen Blick auf die Welt von WP: Es gibt viele moderne PHP-Frameworks und CMS. Keines davon empfiehlt, Plugins / Module / Erweiterungen (oder wie auch immer sie genannt werden) mit Framework-Code zu testen.
Wenn Sie Fabriken verpassen, ein nützliches Feature der Suite, müssen Sie wissen, dass es dort tolle Dinge gibt.
Fallstricke und Schattenseiten
Es gibt einen Fall, in dem der hier vorgeschlagene Workflow nicht funktioniert: Testen von benutzerdefinierten Datenbanken .
In der Tat, es zu schreiben , wenn Sie Standard - Wordpress - Tabellen und Funktionen (auf der untersten Ebene $wpdb
Methoden) Sie nie brauchen tatsächlich Schreibdaten oder Test , wenn Daten tatsächlich in der Datenbank, nur sicher sein , dass richtigen Methoden mit der richtigen Argumenten aufgerufen werden.
Sie können jedoch Plug-ins mit benutzerdefinierten Tabellen und Funktionen schreiben, die Abfragen erstellen, um sie dort zu schreiben, und prüfen, ob diese Abfragen funktionieren, was Sie tun müssen.
In diesen Fällen kann Ihnen die WordPress-Testsuite sehr helfen, und das Laden von WordPress kann in einigen Fällen erforderlich sein, um Funktionen wie auszuführen dbDelta
.
(Es ist nicht nötig zu sagen, dass für Tests eine andere Datenbank verwendet werden muss, oder?)
Glücklicherweise können Sie mit PHPUnit Ihre Tests in "Suites" organisieren, die separat ausgeführt werden können. So können Sie eine Suite für benutzerdefinierte Datenbanktests schreiben, in der Sie die WordPress-Umgebung (oder einen Teil davon) laden und den Rest Ihrer Tests WordPress-frei lassen .
Stellen Sie nur sicher, dass Sie Klassen schreiben, die so viele Datenbankoperationen wie möglich abstrahieren, so dass alle anderen Plug-in-Klassen von ihnen Gebrauch machen, damit Sie mithilfe von Mocks die Mehrheit der Klassen ordnungsgemäß testen können, ohne sich mit der Datenbank zu befassen.
Zum dritten Mal bedeutet das Schreiben von Code, der isoliert leicht zu testen ist, das Schreiben von besserem Code.
phpunit
fehlgeschlagene oder bestandene Tests sehen? Hast du installiertbin/install-wp-tests.sh
?