Ich beginne mit einem Link zum Handbuch und gehe dann auf das ein, was ich auf dem Gebiet gesehen und gehört habe.
Organisation von Phpunit-Testsuiten
Organisation des Modul- / Testordners im Dateisystem
Mein empfohlener Ansatz ist das Kombinieren des Dateisystems mit einer XML-Konfiguration.
tests/
\ unit/
| - module1
| - module2
- integration/
- functional/
mit einem phpunit.xml
mit einem einfachen:
<testsuites>
<testsuite name="My whole project">
<directory>tests</directory>
</testsuite>
</testsuites>
Sie können die Testsuiten aufteilen, wenn Sie möchten, aber das ist eine Projekt-zu-Projekt-Auswahl.
Beim Ausführen phpunit
werden dann ALLE Tests ausgeführt, und beim Ausführen phpunit tests/unit/module1
werden alle Tests von Modul1 ausgeführt.
Organisation des Ordners "Einheit"
Der häufigste Ansatz besteht darin, Ihre source/
Verzeichnisstruktur in Ihrer tests/unit/
Ordnerstruktur zu spiegeln .
Sie haben sowieso eine Testklasse pro Produktionsklasse, daher ist dies ein guter Ansatz in meinem Buch.
In der Dateiorganisation
Es wird sowieso nicht funktionieren, wenn Sie mehr als eine Testklasse in einer Datei haben, also vermeiden Sie diese Gefahr.
- Ich habe keinen Test-Namespace
Es macht das Schreiben des Tests nur ausführlicher, da Sie eine zusätzliche use-Anweisung benötigen. Ich würde also sagen, dass die testClass im selben Namespace wie die Produktionsklasse abgelegt werden sollte, aber das ist nichts, wozu PHPUnit Sie zwingt. Ich habe gerade festgestellt, dass es einfacher ist, ohne Nachteile.
Nur wenige Tests ausführen
phpunit --filter Factory
Führt beispielsweise alle FactoryTests aus, während alle mit der phpunit tests/unit/logger/
Protokollierung verbundenen Elemente ausgeführt werden.
Sie können @group
Tags für Ausgabenummern, Storys oder ähnliches verwenden, aber für "Module" würde ich das Ordnerlayout verwenden.
Mehrere XML-Dateien
Es kann nützlich sein, mehrere XML-Dateien zu erstellen, wenn Sie Folgendes möchten:
- eine ohne Code-Abdeckung
- eine nur für die Unit-Tests (aber nicht für die Funktions- oder Integrations- oder Langzeittests)
- andere häufige "Filter" -Fälle
- PHPBB3 zum Beispiel macht das für
their phpunit.xmls
Codeabdeckung für Ihre Tests
Wie es mit dem Starten eines neuen Projekts mit Tests zusammenhängt:
- Mein Vorschlag ist,
@covers
Tags wie in meinem Blog beschrieben zu verwenden (Nur für Komponententests, decken Sie immer alle nicht öffentlichen Funktionen ab, verwenden Sie immer Cover-Tags.
- Generieren Sie keine Abdeckung für Ihre Integrationstests. Es gibt Ihnen ein falsches Gefühl der Sicherheit.
- Verwenden Sie immer die Whitelist, um Ihren gesamten Produktionscode einzuschließen, damit die Zahlen Sie nicht anlügen!
Autoloading und Bootstrapping Ihrer Tests
Sie benötigen für Ihre Tests kein automatisches Laden. PHPUnit wird sich darum kümmern.
Verwenden Sie das <phpunit bootstrap="file">
Attribut, um Ihren Test-Bootstrap anzugeben. tests/bootstrap.php
ist ein schöner Ort, um es auszudrücken. Dort können Sie Ihren Anwendungs-Autoloader usw. einrichten (oder Ihren Anwendungs-Bootstrap aufrufen).
Zusammenfassung
- Verwenden Sie die XML-Konfiguration für so ziemlich alles
- Separate Unit- und Integrationstests
- Ihre Unit-Test-Ordner sollten die Struktur Ihres Anwendungsordners widerspiegeln
- Um nur bestimmte Tests auszuführen, verwenden Sie
phpunit --filter
oderphpunit tests/unit/module1
- Verwenden Sie den
strict
Modus von Anfang an und schalten Sie ihn niemals aus.
Beispielprojekte zum Anschauen
Grundlegende Verzeichnisstruktur :
Ich habe versucht, den Testcode direkt neben dem zu testenden Code zu halten, buchstäblich im selben Verzeichnis mit einem etwas anderen Dateinamen als die Datei mit dem zu testenden Code. Bisher gefällt mir dieser Ansatz. Die Idee ist, dass Sie keine Zeit und Energie aufwenden müssen, um die Verzeichnisstruktur zwischen Ihrem Code und Ihrem Testcode synchron zu halten. Wenn Sie also den Namen des Verzeichnisses ändern, in dem sich der Code befindet, müssen Sie nicht auch den Verzeichnisnamen für den Testcode suchen und ändern. Dies führt auch dazu, dass Sie weniger Zeit damit verbringen, nach dem Testcode zu suchen, der zu einem Code gehört, da er sich direkt daneben befindet. Dies macht es sogar weniger mühsam, die Datei mit dem Testcode zu erstellen, da Sie nicht zuerst das Verzeichnis mit den Tests finden müssen. Erstellen Sie möglicherweise ein neues Verzeichnis, das mit dem Verzeichnis übereinstimmt, für das Sie Tests erstellen, und erstellen Sie dann die Testdatei. Sie erstellen einfach die Testdatei genau dort.
Ein großer Vorteil davon ist, dass die anderen Mitarbeiter (nicht Sie, weil Sie dies niemals tun würden) das Schreiben von Testcode zunächst weniger vermeiden, weil es einfach zu viel Arbeit ist. Selbst wenn sie Methoden zu vorhandenen Klassen hinzufügen, ist es weniger wahrscheinlich, dass sie keine Tests zum vorhandenen Testcode hinzufügen möchten, da das Auffinden des Testcodes nur wenig reibungslos funktioniert.
Ein Nachteil ist, dass es schwieriger ist, Ihren Produktionscode ohne die dazugehörigen Tests freizugeben. Wenn Sie strenge Namenskonventionen verwenden, ist dies möglicherweise dennoch möglich. Zum Beispiel habe ich ClassName.php, ClassNameUnitTest.php und ClassNameIntegrationTest.php verwendet. Wenn ich alle Unit-Tests ausführen möchte, gibt es eine Suite, die nach Dateien sucht, die auf UnitTest.php enden. Die Integrationstestsuite funktioniert ähnlich. Wenn ich wollte, könnte ich eine ähnliche Technik verwenden, um zu verhindern, dass die Tests für die Produktion freigegeben werden.
Ein weiterer Nachteil dieses Ansatzes besteht darin, dass bei der Suche nach tatsächlichem Code und nicht nach Testcode etwas mehr Aufwand erforderlich ist, um zwischen beiden zu unterscheiden. Aber ich denke, dass dies tatsächlich eine gute Sache ist, da es uns zwingt, den Schmerz der Realität zu spüren, dass Testcode auch Code ist, es seine eigenen Wartungskosten hinzufügt und genauso wichtig ein Teil des Codes ist wie alles andere, nicht nur irgendwo etwas abseits.
Eine Testklasse pro Klasse:
Dies ist für die meisten Programmierer alles andere als experimentell, aber für mich. Ich experimentiere damit, dass nur eine Testklasse pro Klasse getestet wird. In der Vergangenheit hatte ich für jede getestete Klasse ein ganzes Verzeichnis und dann mehrere Klassen in diesem Verzeichnis. Jede Testklasse richtete die zu testende Klasse auf eine bestimmte Art und Weise ein und ließ dann eine Reihe von Methoden mit jeweils einer anderen Behauptung erstellen. Aber dann bemerkte ich, dass bestimmte Bedingungen, in die ich diese Objekte bringen würde, Dinge mit anderen Bedingungen gemeinsam hatten, in die sie aus anderen Testklassen gerieten. Die Duplizierung ist zu umfangreich, daher habe ich angefangen, Abstraktionen zu erstellen, um sie zu entfernen. Der Testcode wurde sehr schwer zu verstehen und zu pflegen. Ich erkannte das, aber ich konnte keine Alternative sehen, die für mich Sinn machte. Nur eine Testklasse pro Klasse zu haben, schien nicht in der Lage zu sein, fast genug Situationen zu testen, ohne überwältigend zu werden, den gesamten Testcode in einer Testklasse zu haben. Jetzt habe ich eine andere Perspektive. Selbst wenn ich recht hatte, ist dies ein großer Dämpfer für andere Programmierer und mich, die die Tests schreiben und pflegen wollen. Jetzt experimentiere ich damit, mich zu zwingen, eine Testklasse pro getesteter Klasse zu haben. Wenn ich in dieser einen Testklasse auf zu viele Dinge stoße, um sie zu testen, experimentiere ich damit, dass dies ein Hinweis darauf ist, dass die getestete Klasse zu viel tut und in mehrere Klassen aufgeteilt werden sollte. Um Duplikate zu entfernen, versuche ich, mich so weit wie möglich an einfachere Abstraktionen zu halten, damit alles in einer lesbaren Testklasse vorhanden ist.
AKTUALISIEREN Ich verwende und mag diesen Ansatz immer noch, aber ich habe eine sehr gute Technik gefunden, um die Menge an Testcode und die Menge an Duplikaten zu reduzieren. Es ist wichtig, wiederverwendbare Assertionsmethoden in die Testklasse selbst zu schreiben, die von den Testmethoden in dieser Klasse häufig verwendet werden. Es hilft mir, die richtigen Arten von Assertionsmethoden zu finden, wenn ich sie als interne DSLs betrachte (etwas, das Onkel Bob fördert, und tatsächlich fördert er das Erstellen interner DSLs). Manchmal können Sie dieses DSL-Konzept sogar noch weiter verfolgen (tatsächlich ein DSL erstellen), indem Sie einen Zeichenfolgenparameter akzeptieren, der einen einfachen Wert hat, der sich darauf bezieht, welche Art von Test Sie durchführen möchten. Zum Beispiel habe ich einmal eine wiederverwendbare Assertionsmethode erstellt, die einen $ left-, $ compareesAs- und einen $ right-Parameter akzeptiert.
$this->assertCmp('a', '<', 'b')
.Ehrlich gesagt kann ich diesen Punkt nicht genug betonen, es ist die gesamte Grundlage, um Schreibtests zu etwas Nachhaltigem zu machen (das Sie und die anderen Programmierer weiterhin tun möchten). Es macht es möglich, dass der Wert, den Tests hinzufügen, überwiegt, was sie wegnehmen. Der Punkt ist nicht, dass Sie genau diese Technik verwenden müssen, der Punkt ist, dass Sie eine Art wiederverwendbarer Abstraktionen verwenden müssen, mit denen Sie kurze und lesbare Tests schreiben können. Es mag so aussehen, als würde ich von der Frage abweichen, aber das bin ich wirklich nicht. Wenn Sie dies nicht tun, werden Sie schließlich in die Falle geraten, mehrere Testklassen pro getesteter Klasse erstellen zu müssen, und die Dinge brechen von dort aus wirklich zusammen.
quelle