Warum Ihre Testsuite elf Minuten braucht
Eine langsame Suite ist eine Suite, die übersprungen wird. Die Zeit steckt fast nie in den Assertions, sondern im Neuaufbau der Datenbank, im Hashen von Passwörtern und in Netzaufrufen.
Eine Suite, die elf Minuten braucht, wird vor dem Push nicht ausgeführt. Sie wird von der CI ausgeführt, nachdem der Kontext verloren ist, was aus einer Korrektur von fünf Sekunden einen Rundgang von zwanzig Minuten macht. Irgendwann pusht jemand über einen roten Build hinweg, weil er ziemlich sicher ist, dass es nichts damit zu tun hat.
Die Geschwindigkeit der Suite ist also kein Komfort für Entwickler. Sie entscheidet, ob die Tests ihre Arbeit tun.
Messen statt raten
php artisan test --profileDas gibt die langsamsten Tests aus. Fast immer macht eine Handvoll Fälle den größten Teil der Uhrzeit aus, und die Behebung ist konkret statt architektonisch.
Die Datenbank, meist der größte Teil
Für jeden Test migrieren. RefreshDatabase hüllt jeden Test in eine
Transaktion und rollt sie zurück, was schnell ist. DatabaseMigrations führt
jede Migration für jeden Test aus, was es nicht ist. In einem Projekt mit
zweihundert Migrationen ist dieser Unterschied das ganze Problem.
Migrationen statt eines Schema-Dumps. Selbst einmal pro Durchlauf dauert das Abspielen von zweihundert Migrationen länger, als ihr Ergebnis zu laden:
php artisan schema:dump --pruneDie Suite lädt dann eine SQL-Datei. Das ist der größte Einzelgewinn in einer gewachsenen Codebasis und kostet einen Befehl.
Factories, die mehr anlegen als der Test braucht. Eine Factory mit einer
has()-Kette, die zwanzig verbundene Datensätze erzeugt, für einen Test, der
ein Feld liest. Legen Sie das Minimum an, und verwenden Sie make() statt
create(), wo nichts die Datenbank berührt.
Passwort-Hashing, womit niemand rechnet
Hashing ist absichtlich langsam – das ist sein Zweck. Eine Suite, die über eine Factory hunderte Nutzer anlegt, zahlt diese Kosten hundertfach.
// tests/TestCase.php oder Pest.php
Hash::driver('bcrypt')->setRounds(4);Bei Suiten mit vielen Nutzer-Fixtures hat das allein Laufzeiten halbiert.
Tests, die ins Netz greifen
Die schädlichste Kategorie, weil sie langsam und unzuverlässig zugleich ist. Ein Test, der eine echte API aufruft, wartet auf den Server eines anderen und scheitert, wenn der einen schlechten Tag hat.
Http::preventStrayRequests();Das gehört in die Basisklasse der Tests. Jeder Test, der einen ungefälschten HTTP-Aufruf macht, scheitert nun sofort und sagt Ihnen welcher – und Sie werden welche finden, von denen Sie nichts wussten.
Dasselbe gilt für Queues, Mail und Storage. Gefälscht sind sie sofort da; echt machen sie Arbeit, über die der Test gar nichts behauptet.
Warten und Pollen
sleep(2) in einem Test, der auf etwas Asynchrones wartet, sind zwei Sekunden
bei jedem Durchlauf, für immer, und es ist entweder zu lang oder – auf einer
langsameren Maschine – nicht lang genug.
Laravels Zeit-Helfer machen Warten überflüssig: Zeit einfrieren, vorspulen, prüfen. Für wirklich asynchrone Arbeit führen Sie die Queue in Tests synchron aus, statt auf einen Worker zu warten.
Browser-Tests, die in einen anderen Topf gehören
Sie sind langsam, weil sie einen echten Browser steuern, und es gibt keinen Trick, der das schnell macht. Die Antwort ist Menge statt Geschwindigkeit: eine kleine Zahl über die Pfade, die zählen, getrennt von der schnellen Suite ausgeführt, damit sie nicht zwischen einer Entwicklerin und ihrer Rückmeldung stehen.
Parallelität, sobald die Tests sie verdienen
php artisan test --parallelNahezu lineare Verbesserung auf einer Mehrkernmaschine – und sie legt jeden Test offen, der still von einem anderen abhing. Gemeinsame Fixture-Dateien, fest verdrahtete IDs, ein Datensatz, dessen Existenz angenommen wird, weil ein früherer Test ihn angelegt hat.
Diese Fehlschläge sind es wert, gefunden zu werden. Ein Test, der nur nach einem anderen besteht, ist kein Test, sondern eine Reihenfolge, und er wäre in der CI ohnehin zufällig gescheitert.
Das Ziel
Unter einer Minute für die Suite, die man vor dem Push ausführt, und alles Langsamere – Browser-Tests, Integration gegen echte Dienste – in einem eigenen Job danach.
Der Maßstab ist nicht Abdeckung und nicht Eleganz. Er ist, ob eine Entwicklerin die Tests ausführt, ohne sich dafür zu entscheiden.
Eine Suite, die niemand ausführt, ist auch eine Suite, hinter der niemand ein Upgrade machen kann. Deshalb ist es die erste Phase eines Upgrades, eine zu bauen, und nicht die letzte. Und wenn die schnelle Suite auf SQLite läuft, während in Produktion MySQL läuft, hat dieser Tausch einen Preis, und der ist nicht immer klein.
