Ich tauche immer tiefer in die Welt der Unit-Tests ein.
Ein Problem, auf das ich gestoßen bin, und hier hätte ich gerne Feedback, ist, wenn man mehrere Testsuiten ausführt, vielleicht liegt es nur an mir, aber ich muss den Parameter –process-isolation verwenden, damit meine Tests bestehen. Ich kann jede meiner Suiten problemlos einzeln ausführen, aber die Ausführung der 6-7 Suiten, die ich bisher mit 180 darauf verteilten Behauptungen habe, schlägt fehl, wenn ich sie ohne –process-isolation ausführe. Das Problem ist, dass die Verwendung dieses Parameters dazu führt, dass der Testlauf 35 Minuten dauert, im Gegensatz zu den üblichen 2,5 Minuten. Das ist eine laaange Wartezeit.
Das Problem hängt mit der Verwendung von verspotteten DI-Containern für bestimmte Tests zusammen, und Container werden nicht ordnungsgemäß neu initialisiert, wenn Testsuiten verkettet ausgeführt werden. Statische Eigenschaften, die auf dem DI-Container festgelegt sind, um auf erwartete Fehler zu testen, führen dazu, dass die Tests in der folgenden Suite fehlschlagen. Der Container hat einen Parameter, der enthaltenes Objekt in einer statischen Variablen enthalten kann, um bei jedem Aufruf dieselbe Instanz zurückzugeben. Ein Singleton in Verkleidung. Und das läuft auf Anwendungsebene gut, es ist nur ein Ärgernis zum Testen.
Ich könnte diesen Containerparameter vermeiden und die Anwendung so codieren, dass sie keine statischen Eigenschaften verwendet, aber das Vermeiden eines nützlichen Sprachkonstrukts zugunsten einer Methodik scheint übertrieben zu sein.
Vielleicht mache ich etwas falsch (ich hoffe es sehr!), aber ich habe den Eindruck, wenn man Tests mit dem SUT in einem sauberen Zustand für jeden Test durchführen möchte, kommt man um –process-isolation nicht herum. Das macht das Testen sehr zeitaufwändig und nimmt ein wenig die Freude daran. Ich habe das Problem etwas umgangen, indem ich Suiten und Tests einzeln ausgeführt habe, wenn ich codiert habe, und die Suite vor größeren Commits im Hintergrund ausgeführt habe.
Ist das, was ich erlebe, normal und gibt es eine Möglichkeit, dem entgegenzuwirken? Wie stellen die Tester da draußen sicher, dass die Testzeit angemessen ist? Wie wird mit der Statik umgegangen, um die Prüfung nicht zu beeinflussen?
Jeder Einblick geschätzt/Kommentar geschätzt.
cweiske
Sie haben mehrere Probleme.
Die erste ist die Prozessisolierung. Normalerweise sollte es nicht notwendig sein und Sie möchten es nur verwenden, um herauszufinden, welcher spezifische Test derjenige ist, der Ihre Tests fatal bricht. Wie Sie selbst bemerkt haben, ist es schrecklich langsam, was Sie nicht beheben können. Sie möchten vielleicht Deaktivieren Sie die Sicherung globaler Variablen was allerdings einige Millisekunden pro Test spart.
Das zweite Problem, das zu Ihrem ersten Problem führt, ist, dass Ihr Code nicht testbar ist, weil statische Variablen während der Tests beibehalten werden – mein am meisten gehasstes Singleton-Problem. Sie können dieses Problem lösen, indem Sie in Ihren Abhängigkeitscontainern eine „Cleanup“- oder „Reset“-Methode bereitstellen. Diese werden von der angerufen setUp() -Methode in Ihrer Haupttestfallklasse und setzen Sie alles auf einen sauberen Zustand zurück.
Geschwindigkeit
Bezüglich der Laufzeit von Tests – ich schrieb kürzlich a Blog-Eintrag herauszufinden, welche Tests zu langsam waren. Im Allgemeinen sind Tests zu langsam, wenn Sie sie nicht ausführen können, nachdem Sie die Datei oder jedes Commit auf Ihrer eigenen Box gespeichert haben. 10 Sekunden sind für mich gerade noch akzeptabel. Je mehr Tests Sie haben, desto langsamer werden sie ausgeführt.
Wenn Sie wirklich 35 Minuten Zeit haben, teilen Sie Ihre Tests in sinnvolle Gruppen auf, damit Sie die notwendigen auf Ihrem eigenen Computer ausführen können – nur die Tests, die den von Ihnen geänderten Code testen. Pyrus, der PEAR-Installer der nächsten Generation, hat die raffinierte Funktion dazu automatisch die erforderlichen Tests erkennen und ausführen, je nachdem, welche Dateien Sie geändert haben. PHPUnit hat das nicht, aber man kann das per Hand emulieren und phpunit --group .. 🙂
Achten Sie immer darauf, Webdienste und Datenbanken zu verspotten oder zumindest die Datenbank nur mit den notwendigen Daten für jeden einzelnen Test auszuführen. 3 Sekunden auf eine Webservice-Antwort in einem Test zu warten, der überprüft, ob Sie den Benutzer in der Datenbank speichern können, ist etwas, das Sie nie wollen.
Sehr aufschlussreich, danke für das Feedback, schön zu wissen, dass es Möglichkeiten gibt, meine Probleme zu beheben. Ich schätze.
– stefgosselin
29. Mai 2011 um 13:06 Uhr
Ich kann nicht wirklich mehr hinzufügen, außer zu betonen, dass die Lösung des Problems, das die Ausführung von Tests in einem einzigen Prozess blockiert – obwohl es scheinbar übertrieben ist – Sie zu einem viel glücklicheren Tester machen wird. Unser Legacy-Code verwendet viele statische Variablen (mühsam zum Testen). Anstatt sie alle auf einmal zu reparieren, habe ich einfache “Setup-Aktionen” geschrieben, die Setup/Teardown für jeden Test durchführen. Es ist ein Schrotflintenansatz, der uns Zeit verschafft, jetzt zu testen und später mit einem Skalpell zurückzukommen, um jeden Fall elegant zu handhaben. 🙂
– David Harkness
29. Mai 2011 um 20:19 Uhr
Kam hierher, um einen großen Daumen nach oben zu posten Blogeintrag. Die Verwendung der von Ihnen erwähnten Phing-Aufgabe hat mich auf den Test aufmerksam gemacht, der erstickte. Ich habe das Hinzufügen einer Reset-Methode im Container zur Kenntnis genommen, würde es aber vorziehen, keinen testbezogenen Code mit Produktionscode zu haben. Obwohl diese Methode leicht sein könnte mit sed entfernt mit svn Pre-Commit-Hook. Die Testzeit ist wieder auf vernünftige 96 Sekunden zurückgegangen, ich werde DB-Tests noch etwas optimieren, indem ich diese Woche sqlite verwende.
Eines der Dinge, die ich normalerweise mache, wenn ich mit MySQL statt mit SQLite teste :memory: füge ich hinzu Hash::setRounds(5); Innerhalb tests/CreatesApplication.php Eigenschaft wie diese. Ich habe die Erfahrung gemacht, dass dies die Tests besonders mit MySQL so viel schneller macht:
public function createApplication()
{
$app = require __DIR__ . '/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
// TODO: DON'T FORGET TO IMPORT HASH OBJECT ON TOP
Hash::setRounds(5);
return $app;
}
Es sieht aus wie Hash ist eine Laravel-Klasse – also nur relevant, wenn Sie Laravel-Code testen, der Passwort-Hashing verwendet.
– bdsl
13. Mai 2021 um 19:47 Uhr
Orod Semsarzadeh
Ein paar Tricks;
Filtern Sie Ihre Testfälle, wenn Sie beispielsweise eine Datei testen möchten, müssen Sie dies nur tun
phpunit --filter 'Default_My_Test'
Entfernen der Codeabdeckung in Ihrer phpunit.xml-Datei. Wenn Sie eine Codeabdeckung erhalten möchten, tun Sie Folgendes:
phpunit --coverage-html ./report reportTest
12988300cookie-checkphpunit – das Testen ist quälend langsamyes