Beschleunigung von RSpec-Tests in einer großen Rails-Anwendung


90

Ich habe eine Rails-Anwendung mit über 2.000 Beispielen in meinen RSpec-Tests. Es ist natürlich eine große Anwendung und es gibt viel zu testen. Das Ausführen dieser Tests zu diesem Zeitpunkt ist sehr ineffizient. Da dies so lange dauert, werden wir fast davon abgehalten, sie zu schreiben, bevor wir einen neuen Build veröffentlichen. Ich habe --profile zu meinen spec.opts hinzugefügt, um die am längsten laufenden Beispiele zu finden, und es gibt mindestens 10 davon, deren Ausführung durchschnittlich 10 Sekunden dauert. Ist das unter RSpec-Experten normal? Sind 10 Sekunden für ein Beispiel völlig zu lang? Mir ist klar, dass es bei 2.000 Beispielen nicht trivial sein wird, alles gründlich zu testen - aber an diesem Punkt sind 4 Stunden ein bisschen lächerlich.

Welche Zeiten sehen Sie für Ihre am längsten laufenden Beispiele? Was kann ich tun, um meine vorhandenen Spezifikationen zu beheben, um Engpässe herauszufinden und die Dinge zu beschleunigen? Jede Minute würde an dieser Stelle wirklich helfen.


1
Sind die langsamen Tests Integrationstests? Schlagen sie eine Datenbank? Wenn ja, wie oft wird die Datenbank neu geladen und können Sie die Datenbank verspotten?
Kaleb Pederson

1
Können Sie nur einen Teil der Spezifikationen ausführen, die für den Teil relevant sind, an dem Sie arbeiten, ähnlich dem Autotest von SeattleRB? Haben Sie einen Continuous Integration Server, auf dem alle Tests ausgeführt werden können?
Andrew Grimm

Denken Sie auch daran, dass alle Dinge relativ sind. Ich habe gehört, "grrr, unsere Testsuite dauert ewig" für 20 Minuten ... und 16-20 Stunden. Es liegt alles im Auge des Betrachters. 10 Sekunden für einen bestimmten Test bedeuten häufig einen Komponententest, der wie unten erwähnt zu einem Integrationstest geworden ist.
Michael Durrant

Ein Vorschlag für diese Art von Problem: Verwenden Sie perftools.rbzusammen mit Ihrem Test-Framework, um zu verstehen, was die meiste Zeit Ihrer Zeit verbraucht. Nehmen Sie die Top 10 Anrufe an und versuchen Sie, sie zu eliminieren / zu überfliegen. Dann wiederhole, bis du glücklich bist.
Aledalgrande

Antworten:


118

10 Sekunden sind eine sehr lange Zeit, um einen einzelnen Test auszuführen. Mein Bauchgefühl ist, dass Ihr Spezifikationsziel gleichzeitig Unit- und Integrationstests ausführt. Dies ist eine typische Sache, in die Projekte fallen, und irgendwann müssen Sie diese technische Verschuldung überwinden, wenn Sie mehr und schneller produzieren möchten. Es gibt eine Reihe von Strategien, die Ihnen dabei helfen können ... und ich werde einige empfehlen, die ich in der Vergangenheit verwendet habe.

1. Trennen Sie die Einheit von den Integrationstests

Das erste, was ich tun würde, ist, die Einheit von den Integrationstests zu trennen. Sie können dies entweder tun durch:

  1. Verschieben (in separate Ordner unter dem Spezifikationsverzeichnis) - und Ändern der Rake-Ziele
  2. Markieren Sie sie (mit rspec können Sie Ihre Tests markieren)

Die Philosophie besagt, dass Sie möchten, dass Ihre regulären Builds schnell sind - sonst sind die Leute nicht allzu glücklich, sie oft auszuführen. Also zurück in dieses Gebiet. Lassen Sie Ihre regelmäßigen Tests schnell laufen und verwenden Sie einen kontinuierlichen Integrationsserver, um den vollständigeren Build auszuführen.

Ein Integrationstest ist ein Test, der externe Abhängigkeiten beinhaltet (z. B. Datenbank, WebService, Warteschlange und einige würden FileSystem argumentieren). Ein Komponententest testet nur das spezifische Codeelement, das Sie überprüfen möchten. Es sollte schnell laufen (9000 in 45 Sekunden sind möglich), dh das meiste sollte im Speicher laufen.

2. Konvertieren Sie Integrationstests in Komponententests

Wenn der Großteil Ihrer Komponententests kleiner als Ihre Integrationstestsuite ist, liegt ein Problem vor. Dies bedeutet, dass Inkonsistenzen leichter auftreten. Erstellen Sie ab hier weitere Komponententests, um Integrationstests zu ersetzen. Sie können Folgendes tun, um diesen Prozess zu unterstützen:

  1. Verwenden Sie anstelle der eigentlichen Ressource ein Spott-Framework. Rspec verfügt über ein eingebautes Verspottungs-Framework.
  2. Führen Sie rcov in Ihrer Unit-Test-Suite aus. Verwenden Sie diese Option, um festzustellen, wie gründlich Ihre Unit-Test-Suite ist.

Wenn Sie einen oder mehrere geeignete Komponententests zum Ersetzen eines Integrationstests haben, entfernen Sie den Integrationstest. Doppelte Tests verschlimmern die Wartung nur.

3. Verwenden Sie keine Geräte

Spielpaarungen sind böse. Verwenden Sie stattdessen eine Fabrik (Maschinist oder Factorybot). Diese Systeme können anpassungsfähigere Datengraphen erstellen und, was noch wichtiger ist, speicherinterne Objekte erstellen, die Sie verwenden können, anstatt Dinge aus einer externen Datenquelle zu laden.

4. Fügen Sie Überprüfungen hinzu, um zu verhindern, dass Komponententests zu Integrationstests werden

Jetzt, da Sie schnellere Tests durchgeführt haben, ist es Zeit, Überprüfungen durchzuführen, um zu verhindern, dass diese erneut auftreten.

Es gibt Bibliotheken, die Affen aktiv patchen, um einen Fehler auszulösen, wenn versucht wird, auf die Datenbank zuzugreifen (UnitRecord).

Sie können auch Pairing und TDD ausprobieren, um Ihr Team zu zwingen, schnellere Tests zu schreiben, weil:

  1. Jemand überprüft - damit niemand faul wird
  2. Eine ordnungsgemäße TDD erfordert eine schnelle Rückmeldung. Langsame Tests machen das Ganze nur schmerzhaft.

5. Verwenden Sie andere Bibliotheken, um das Problem zu lösen

Jemand erwähnte Spork (beschleunigt die Ladezeiten für die Testsuite unter Rails3), hydra / parallel_tests - um Unit-Tests parallel (über mehrere Kerne hinweg) durchzuführen.

Dies sollte wahrscheinlich LETZT verwendet werden. Ihr eigentliches Problem ist in Schritt 1, 2, 3 zu lösen. Lösen Sie das Problem, und Sie sind besser in der Lage, zusätzliche Infrastrukturen auszurollen.


Gute Antwort. Es ist sehr richtig, dass keine fortschrittlichen Tools dazu beitragen können, schlechte Tests schnell durchzuführen. Versuchen Sie also, nur gute zu schreiben.
Yura Omelchuk

5
Ich bin mit Ihrem dritten Punkt nicht einverstanden, dass Sie keine Geräte verwenden sollten. Bei richtiger Verwendung sind sie schneller als Fabriken, da Sie keine Objekte erstellen müssen. Ihre Daten sind da, Sie müssen sie nicht bei jedem Testfall erstellen.
Wallerjake

3
Das über schnellere Fabriken ist ein Witz, oder?
Mike Szyndel


16

Ein großartiges Kochbuch zur Verbesserung der Leistung Ihrer Testsuite finden Sie in der Präsentation von Grease Your Suite .

Er dokumentiert eine 45-fache Beschleunigung der Laufzeit der Testsuite mithilfe von Techniken wie:


Ich mag die Links, aber nicht die Präsentation. Karteikarten für die Rede einer Person sind eine Aufforderung an den Redner, das Fleisch der Präsentation auszufüllen.
Brian Maltzan

Diese Präsentation ist nicht mehr aktiv. fast_context beschleunigt mehrere Sollteblöcke. Der Schnellclip (dieser Link ist ebenfalls tot) beschleunigt anscheinend die Büroklammer. Hydra ist für parallel_tests und Gorgon veraltet. bootnap hat Dateisystem-Optimierungen und ist jetzt in Rails enthalten. Neuere Versionen von Ruby haben die Zuordnung und GC verbessert.
Wiederholung

5

Sie können Spork verwenden. Es hat Unterstützung für 2.3.x,

https://github.com/sporkrb/spork

oder ./script/spec_server, der möglicherweise für 2.x funktioniert

Sie können auch die Datenbankkonfiguration bearbeiten (was die Datenbankabfragen usw. wesentlich beschleunigt), wodurch auch die Leistung für Tests erhöht wird.


Spork ist großartig. Es funktioniert auch auf Rails 3 mit ein bisschen Hacking. Bei Spork on Rails 3 ist jedoch zu beachten, dass Änderungen an den Controllern nicht erfasst werden. Sie müssen sie daher von Zeit zu Zeit neu starten. Trotzdem ist Spork wirklich großartig, die Geschwindigkeitsverbesserung der Tests ist sehr bedeutend.
AboutRuby

4
Spork dient nur zur Beschleunigung des Startvorgangs, nicht zum Testen. Bei vielen Spezifikationen ist der Vorteil also unbedeutend.
Reactormonk

Im Gegensatz zu den obigen Kommentaren kann Spork verwendet werden, um Tests sowie den Start gemäß Railscast zu beschleunigen . Railscasts.com/episodes/285-spork . Meine rspec-Testsuite auf einer sehr großen App wurde massiv verbessert. (Reduziert von 192 Sekunden auf 10 Sekunden!)
Jamesc

3

10 Sekunden pro Beispiel scheinen eine sehr lange Zeit zu sein. Ich habe noch nie eine Spezifikation gesehen, die länger als eine Sekunde gedauert hat, und die meisten brauchen weit weniger. Testen Sie Netzwerkverbindungen? Datenbank schreibt? Dateisystem schreibt?

Verwenden Sie Mocks und Stubs so oft wie möglich - sie sind viel schneller als das Schreiben von Code, der in die Datenbank gelangt. Leider braucht das Verspotten und Stubben auch mehr Zeit zum Schreiben (und es ist schwieriger, es richtig zu machen). Sie müssen die Zeit, die Sie für das Schreiben von Tests aufgewendet haben, mit der Zeit abwägen, die Sie für das Ausführen von Tests aufgewendet haben.

Ich stimme Andrew Grimms Kommentar über die Untersuchung eines CI-Systems zu, mit dem Sie möglicherweise Ihre Testsuite parallelisieren können. Für etwas dieser Größe könnte es die einzig praktikable Lösung sein.


Ja, wenn es 10 Sekunden sind, würde ich es profilieren und sehen, wo der Engpass ist
Rogerdpack

1
Ich habe ein riesiges Projekt und das Ausführen eines einzelnen Rspec-Tests dauert fast 15 bis 20 Sekunden.
ExiRe

2

Einige Leute haben Hydra oben erwähnt. Wir haben es in der Vergangenheit mit großem Erfolg eingesetzt. Ich habe kürzlich den Prozess der Inbetriebnahme von Hydra dokumentiert: http://logicalfriday.com/2011/05/18/faster-rails-tests-with-hydra/

Ich stimme dem Gefühl zu, dass diese Art von Technik nicht als Ersatz für das Schreiben von Tests verwendet werden sollte, die standardmäßig gut strukturiert und schnell sind.





-4

Löschen Sie die vorhandene Testsuite. Wird unglaublich effektiv sein.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.