it-swarm.com.de

Wie lassen wir Unit-Tests schnell laufen?

Wir haben den Punkt in unserem Projekt erreicht, an dem wir fast tausend Tests haben und die Leute aufgehört haben, sie auszuführen, bevor sie einchecken, weil es so lange dauert. Bestenfalls führen sie die Tests durch, die für den Code relevant sind, den sie geändert haben, und im schlimmsten Fall checken sie ihn einfach ein, ohne ihn zu testen.

Ich glaube, dieses Problem ist auf die Tatsache zurückzuführen, dass die Lösung auf 120 Projekte angewachsen ist (wir führen normalerweise viel kleinere Projekte durch und dies ist erst das zweite Mal, dass wir TDD ordnungsgemäß ausführen) und die Build + Test-Zeit auf etwa zwei bis drei Minuten angewachsen ist auf den kleineren Maschinen.

Wie verringern wir die Laufzeit der Tests? Gibt es Techniken? Mehr vortäuschen? Weniger vortäuschen? Vielleicht sollten die größeren Integrationstests nicht automatisch ausgeführt werden, wenn alle Tests ausgeführt werden?

Edit : Als Antwort auf einige der Antworten verwenden wir bereits CI und einen Build-Server. Deshalb weiß ich, dass die Tests fehlschlagen. Das Problem (eigentlich ein Symptom) ist, dass wir immer wieder Nachrichten über fehlgeschlagene Builds erhalten. Teiltests durchzuführen ist etwas, was die meisten Leute tun, aber nicht alle. und in Bezug auf die Tests sind sie eigentlich ziemlich gut gemacht, sie verwenden Fälschungen für alles und es gibt überhaupt kein IO).

40
Ziv

Eine mögliche Lösung wäre, den Testteil von den Entwicklungsmaschinen auf ein kontinuierliches Integrationssetup ( Jenkins zum Beispiel) zu verschieben, indem eine Versionskontrollsoftware mit einem gewissen Geschmack ( git , - verwendet wird svn , etc ...).

Wenn neuer Code geschrieben werden muss, erstellt der angegebene Entwickler einen Zweig für alles, was er im Repository tut. Alle Arbeiten werden in diesem Zweig ausgeführt und sie können ihre Änderungen jederzeit in den Zweig übernehmen, ohne die Hauptcodezeile durcheinander zu bringen.

Wenn die angegebene Funktion, Fehlerbehebung oder was auch immer sie gerade bearbeiten, abgeschlossen ist, kann dieser Zweig wieder in den Trunk zusammengeführt werden (oder wie auch immer Sie es vorziehen), in dem alle Komponententests ausgeführt werden. Wenn ein Test fehlschlägt, wird die Zusammenführung abgelehnt und der Entwickler benachrichtigt, damit er die Fehler beheben kann.

Sie können Ihren CI-Server auch die Komponententests für jeden Feature-Zweig ausführen lassen, während Commits durchgeführt werden. Auf diese Weise kann der Entwickler einige Änderungen vornehmen, den Code festschreiben und den Server die Tests im Hintergrund ausführen lassen, während er weiterhin an zusätzlichen Änderungen oder anderen Projekten arbeitet.

Eine gute Anleitung für eine Möglichkeit, ein solches Setup durchzuführen, finden Sie hier (git-spezifisch, sollte aber für andere Versionskontrollsysteme funktionieren): http://nvie.com/posts/a-successful-git-branching- Modell /

50
Mike

Die meisten Unit-Tests sollten jeweils weniger als 10 Millisekunden dauern. 'Fast tausend Tests' zu haben ist nichts und sollte vielleicht einige Sekunden dauern, um ausgeführt zu werden.

Wenn dies nicht der Fall ist, sollten Sie aufhören, hochgekoppelte Integrationstests zu schreiben (es sei denn, der Code benötigt dies) und gute Komponententests schreiben (beginnend mit gut entkoppelter Code und ordnungsgemäße Verwendung von Fakes/Mocks/Stubs/etc). Diese Kopplung wirkt sich auch auf die Testqualität und die Zeit aus, die zum Schreiben erforderlich ist. Es geht also nicht nur darum, die Testlaufzeit zu verkürzen.

33
Telastyn

Es gibt verschiedene Ansätze, mit denen ich ähnliche Probleme gelöst habe:

  1. Überprüfen Sie die Ausführungszeit und finden Sie alle langsamsten Tests und dann analysieren Sie, warum die Ausführung so lange dauert.
  2. Sie haben 100 Projekte. Müssen Sie sie möglicherweise nicht jedes Mal erstellen und testen? Könntest du alles nur in einer Nacht bauen? Erstellen Sie mehrere 'schnelle' Build-Konfigurationen für den täglichen Gebrauch. Der CI-Server wird nur eine begrenzte Anzahl von Unittests-Projekten ausführen, die sich auf "heiße" Teile Ihres aktuellen Entwicklungsprozesses beziehen.
  3. Verspotten und isolieren Sie alles, was Sie könnten Vermeiden Sie Festplatten-/Netzwerk-E/A, wann immer dies möglich ist
  4. Wenn es nicht möglich ist, solche Vorgänge zu isolieren, haben Sie möglicherweise Integrationstests? Vielleicht könnten Sie Integrationstests nur für Nacht-Builds planen?
  5. Überprüfen Sie alle gelegentlichen Singletons, die Verweise auf Instanzen/Ressourcen enthalten und Speicher verbrauchen. Dies kann zu Leistungseinbußen führen, während alle Tests ausgeführt werden.

Darüber hinaus können Sie die folgenden Tools verwenden, um Ihr Leben zu vereinfachen und Tests schneller durchzuführen

  1. Gated Commit Einige CI-Server können so konfiguriert werden, dass sie Builds und Tests durchführen, bevor Code in das Quell-Repository übertragen wird. Wenn jemand Code festschreibt, ohne zuvor alle Tests ausgeführt zu haben, der auch fehlgeschlagene Tests enthält, wird er abgelehnt und an den Autor zurückgegeben.
  2. Konfigurieren Sie den CI-Server m Tests parallel auszuführen: Verwenden Sie mehrere Maschinen oder Prozesse. Beispiele sind pnunit und CI-Konfiguration mit mehreren Knoten.
  3. Plug-in für kontinuierliche Tests für Entwickler, die automatisch alle Tests während des Schreibens von Code ausführen.
16
Akim

0. Hören Sie Ihren Programmierern zu.

Wenn sie die Tests nicht ausführen, bedeutet dies, dass sie die Kosten (Warten auf die Ausführung der Tests, Behandeln falscher Fehler) als höher als den Wert (sofortiges Erkennen von Fehlern) wahrnehmen. Verringern Sie die Kosten, erhöhen Sie den Wert, und die Mitarbeiter führen die Tests ständig durch.

1. Machen Sie Ihre Tests 100% zuverlässig.

Wenn Sie jemals Tests haben, die mit falschen Negativen fehlschlagen, kümmern Sie sich sofort darum. Reparieren Sie sie, ändern Sie sie, beseitigen Sie sie, was auch immer erforderlich ist, um 100% Zuverlässigkeit zu gewährleisten. (Es ist in Ordnung, eine Reihe unzuverlässiger, aber dennoch nützlicher Tests zu haben, die Sie separat ausführen können, aber der Hauptteil der Tests muss zuverlässig sein.)

2. Ändern Sie Ihre Systeme, um sicherzustellen, dass alle Tests ständig bestanden werden.

Verwenden Sie kontinuierliche Integrationssysteme, um sicherzustellen, dass nur übergebene Commits in den Haupt-/offiziellen/Freigabe-/beliebigen Zweig integriert werden.

3. Ändern Sie Ihre Kultur, um 100% bestandene Tests zu bewerten.

Lehren Sie die Lektion, dass eine Aufgabe erst dann "erledigt" ist, wenn 100% der Tests bestanden sind und sie in den Haupt-/offiziellen/Release-/was auch immer-Zweig integriert wurden.

4. Machen Sie die Tests schnell.

Ich habe an Projekten gearbeitet, bei denen Tests eine Sekunde dauern, und an Projekten, bei denen sie den ganzen Tag dauern. Es besteht eine starke Korrelation zwischen der Zeit, die zum Ausführen von Tests benötigt wird, und meiner Produktivität.

Je länger Tests dauern, desto seltener werden sie ausgeführt. Das bedeutet, dass Sie länger brauchen, ohne Feedback zu den vorgenommenen Änderungen zu erhalten. Dies bedeutet auch, dass Sie zwischen den Commits länger bleiben. Häufigeres Festschreiben bedeutet kleinere Schritte, die einfacher zusammenzuführen sind. Das Festschreiben der Historie ist einfacher zu verfolgen. Es ist einfacher, einen Fehler in der Geschichte zu finden. Auch das Zurückrollen ist einfacher.

Stellen Sie sich Tests vor, die so schnell ausgeführt werden, dass es Ihnen nichts ausmacht, sie bei jeder Kompilierung automatisch auszuführen.

Schnelle Tests können schwierig sein (das hat das OP gefragt, richtig!). Entkopplung ist der Schlüssel. Mocks/Fakes sind in Ordnung, aber ich denke, Sie können es besser machen, indem Sie Refactoring durchführen, um Mocks/Fakes unnötig zu machen. Siehe Arlo Belshees Blog, beginnend mit http://arlobelshee.com/post/the-no-mocks-book .

5. Machen Sie Tests nützlich.

Wenn die Tests nicht fehlschlagen, wenn Sie es vermasseln, worum geht es dann? Bringen Sie sich selbst bei, Tests zu schreiben, die die Fehler auffangen, die Sie wahrscheinlich verursachen. Dies ist eine Fähigkeit für sich und wird viel Aufmerksamkeit erfordern.

12
Jay Bazuzi

Ein paar Minuten sind für Unit-Tests in Ordnung. Beachten Sie jedoch, dass es drei Haupttypen von Tests gibt:

  1. Unit-Tests - Testen Sie jede "Unit" (Klasse oder Methode) unabhängig vom Rest des Projekts
  2. Integrationstests - Testen Sie das gesamte Projekt, indem Sie normalerweise das Programm aufrufen. Einige Projekte, die ich gesehen habe, kombinieren dies mit Regressionstests. Hier wird deutlich weniger verspottet als bei Unit-Tests
  3. Regressionstests - Testen Sie das abgeschlossene Projekt als Ganzes, da die Testsuite ein Endbenutzer ist. Wenn Sie eine Konsolenanwendung haben, würden Sie die Konsole verwenden, um das Programm auszuführen und zu testen. Sie setzen Interna diesen Tests niemals aus, und jeder Endbenutzer Ihres Programms sollte (theoretisch) in der Lage sein, Ihre Regressionstestsuite auszuführen (auch wenn dies niemals der Fall sein wird).

Diese sind in der Reihenfolge ihrer Geschwindigkeit aufgeführt. Unit-Tests sollten schnell sein. Sie werden nicht jeden Fehler erkennen, aber sie stellen fest, dass das Programm anständig vernünftig ist. Unit-Tests sollten in maximal 3 Minuten oder mit anständiger Hardware ausgeführt werden. Sie sagen, Sie haben nur 1000 Unit-Tests, die 2-3 Minuten dauern? Nun, das ist wahrscheinlich in Ordnung.

Dinge zu überprüfen:

  • Stellen Sie jedoch sicher, dass Ihre Komponententests und Integrationstests getrennt sind. Integrationstests werden immer langsamer sein.

  • Stellen Sie sicher, dass Ihre Komponententests parallel ausgeführt werden. Es gibt keinen Grund für sie, dies nicht zu tun, wenn es sich um echte Komponententests handelt

  • Stellen Sie sicher, dass Ihre Komponententests "abhängigkeitsfrei" sind. Sie sollten niemals auf eine Datenbank oder das Dateisystem zugreifen

Abgesehen davon klingen Ihre Tests im Moment nicht schlecht. Als Referenz hat einer meiner Freunde in einem Microsoft-Team 4.000 Komponententests, die auf anständiger Hardware in weniger als 2 Minuten ausgeführt werden (und es ist ein kompliziertes Projekt). Es ist möglich, schnelle Unit-Tests durchzuführen. Das Beseitigen von Abhängigkeiten (und das Verspotten nur so viel wie nötig) ist die Hauptsache, um Geschwindigkeit zu erreichen.

4
Earlz

Trainieren Sie Ihre Entwickler in Personal Software Process (PSP) und helfen Sie ihnen, ihre Leistung durch mehr Disziplin zu verstehen und zu verbessern. Das Schreiben von Code hat nichts damit zu tun, die Finger auf eine Tastatur zu schlagen und anschließend eine Kompilierungs- und Check-in-Taste zu drücken.

PSP war in der Vergangenheit sehr beliebt, als das Kompilieren von Code viel Zeit in Anspruch nahm (Stunden/Tage auf einem Mainframe, sodass jeder den Compiler gemeinsam nutzen musste). Aber als persönliche Workstations leistungsfähiger wurden, akzeptierten wir alle den Prozess:

  1. geben Sie einen Code ein, ohne nachzudenken
  2. klicken Sie auf Build/Compile
  3. korrigieren Sie Ihre Syntax, damit sie kompiliert wird
  4. führen Sie Tests durch, um festzustellen, ob das, was Sie geschrieben haben, tatsächlich Sinn macht

Wenn Sie überlegen, bevor Sie tippen, und dann nach dem Tippen überprüfen, was Sie geschrieben haben, können Sie die Anzahl der Fehler reduzieren, bevor Sie eine Build- und Testsuite ausführen. Lernen Sie, nicht 50 Mal am Tag auf Build zu drücken, sondern vielleicht ein- oder zweimal. Dann ist es weniger wichtig, dass Ihre Build- und Testzeit einige Minuten länger dauert.

3
Bart Koopman

Ein möglicher Weg: Teilen Sie Ihre Lösung. Wenn eine Lösung 100 Projekte hat, ist sie ziemlich unüberschaubar. Nur weil zwei Projekte (z. B. A und B) gemeinsamen Code aus einem anderen Projekt (z. B. Lib) verwenden, bedeutet dies nicht, dass sie sich in derselben Lösung befinden müssen.

Stattdessen können Sie Lösung A mit den Projekten A und Lib sowie Lösung B mit den Projekten B und Lib erstellen.

3
svick

Ich bin in einer ähnlichen Situation. Ich habe Unit-Tests, die die Kommunikation mit dem Server testen. Sie testen das Verhalten mit Zeitüberschreitungen, dem Abbrechen von Verbindungen usw. Der gesamte Testsatz dauert 7 Minuten.

7 Minuten sind eine relativ kurze Zeit, aber Sie werden sie nicht vor jedem Commit ausführen.

Wir haben auch eine Reihe von automatisierten UI-Tests, deren Laufzeit 2 Stunden beträgt. Es ist nicht etwas, das Sie jeden Tag auf Ihrem Computer ausführen möchten.

Also, was tun?

  1. Das Ändern der Tests ist normalerweise nicht sehr effektiv.
  2. Führen Sie vor dem Festschreiben nur die relevanten Tests aus.
  3. Führen Sie alle Ihre Tests jeden Tag (oder mehrmals täglich) auf einem Build-Server aus. Dies gibt Ihnen auch die Möglichkeit, Berichte zur Nizza-Code-Abdeckung und Code-Analyse zu erstellen.

Das Wichtigste ist: Alle Ihre Tests sollten häufig ausgeführt werden, da es wichtig ist, die Fehler zu finden. Es ist jedoch nicht unbedingt erforderlich, sie vor dem Festschreiben zu finden.

2
Sulthan

Obwohl Ihre Beschreibung des Problems keinen gründlichen Einblick in die Codebasis gibt, kann ich mit Sicherheit sagen, dass Ihr Problem zweifach ist.

Lernen Sie, die richtigen Tests zu schreiben.

Sie sagen, Sie haben fast tausend Tests und Sie haben 120 Projekte. Angenommen, höchstens die Hälfte dieser Projekte sind Testprojekte, dann haben Sie 1000 Tests für 60 Produktionscode-Projekte. Das gibt dir ungefähr 16-17 Tests pro Projekt !!!

Das ist wahrscheinlich die Anzahl der Tests, die ich in einem Produktionssystem für ungefähr 1-2 Klassen durchführen müsste. Wenn Sie also nicht nur 1-2 Klassen in jedem Projekt haben (in diesem Fall ist Ihre Projektstruktur zu feinkörnig), sind Ihre Tests zu groß und decken zu viel Boden ab. Sie sagen, dies ist das erste Projekt, bei dem Sie TDD ordnungsgemäß ausführen. Die Zahlen, die Sie angeben, zeigen an, dass dies nicht der Fall ist und Sie keine TDD-Eigenschaft ausführen.

Sie müssen lernen, die richtigen Tests zu schreiben, was wahrscheinlich bedeutet, dass Sie zunächst lernen müssen, wie Sie den Code testbar machen. Wenn Sie nicht die Erfahrung innerhalb des Teams finden, um dies zu tun, würde ich vorschlagen, Hilfe von außen einzustellen, z. in Form von ein oder zwei Beratern, die Ihrem Team über einen Zeitraum von 2-3 Monaten helfen, das Schreiben von testbarem Code zu lernen, und kleine minimale Unit-Tests.

Zum Vergleich: In dem .NET-Projekt, an dem ich gerade arbeite, können wir in weniger als 10 Sekunden ungefähr 500 Komponententests durchführen (und das wurde nicht einmal auf einem Hochleistungscomputer gemessen). Wenn dies Ihre Zahlen wären, hätten Sie keine Angst, diese von Zeit zu Zeit vor Ort zu betreiben.

Erfahren Sie, wie Sie die Projektstruktur verwalten.

Sie haben die Lösung in 120 Projekte unterteilt. Das ist nach meinen Maßstäben eine erstaunliche Anzahl von Projekten.

Wenn es also Sinn macht, tatsächlich so viele Projekte zu haben (was meiner Meinung nach nicht der Fall ist - aber Ihre Frage liefert nicht genügend Informationen, um ein qualifiziertes Urteil darüber zu fällen), müssen Sie die Projekte in kleinere Komponenten aufteilen, die kann separat erstellt, versioniert und bereitgestellt werden. Wenn ein Entwickler eine Einheit der Testsuite ausführt, muss er nur die Tests ausführen, die sich auf die Komponente beziehen, an der er gerade arbeitet. Der Build-Server sollte sicherstellen, dass alles korrekt integriert ist.

Die Aufteilung eines Projekts in mehrere Komponenten, die separat erstellt, versioniert und bereitgestellt werden, erfordert meiner Erfahrung nach ein sehr ausgereiftes Entwicklungsteam, ein Team, das ausgereifter ist, als ich das Gefühl habe, dass Ihr Team es ist.

In jedem Fall müssen Sie jedoch etwas an der Projektstruktur ändern. Teilen Sie die Projekte entweder in separate Komponenten auf oder beginnen Sie mit dem Zusammenführen von Projekten.

Fragen Sie sich, ob Sie wirklich 120 Projekte brauchen?

p.s. Vielleicht möchten Sie NCrunch ausprobieren. Es ist ein Visual Studio-Plug-In, das Ihren Test automatisch im Hintergrund ausführt.

1
Pete

Probleme, die ich gesehen habe:

a) Verwenden von IOC zum Aufbau von Testelementen. 70 Sekunden -> 7 Sekunden durch Entfernen des Containers.

b) Nicht alle Klassen verspotten. Halten Sie Ihre Komponententests auf ein einzelnes Element beschränkt. Ich habe Tests gesehen, die durch ein paar Klassen streifen. Dies sind keine Komponententests und es ist viel wahrscheinlicher, dass sie brechen.

c) Profilieren Sie sie, um herauszufinden, was passiert ist. Ich stellte fest, dass der Konstruktor Dinge baute, die ich nicht brauchte, also lokalisierte ich sie und reduzierte die Laufzeiten.

d) Profil. Vielleicht ist der Code nicht so gut und Sie können durch eine Überprüfung eine gewisse Effizienz erzielen.

e) Entfernen Sie Abhängigkeiten. Wenn Sie die ausführbare Testdatei klein halten, wird die Ladezeit verkürzt. Verwenden Sie eine Schnittstellenbibliothek und IOC Container), um Ihre endgültige Lösung auszuführen. In Ihren Haupttestprojekten sollte jedoch nur die Schnittstellenbibliothek definiert sein. Dies stellt die Trennung sicher, stellt sicher, dass das Testen einfacher ist, und führt auch Ihren Test durch Fußabdruck kleiner.

0
Waratah

JUnit-Tests sollen normalerweise schnell sein, aber einige von ihnen müssen einfach einige Zeit in Anspruch nehmen, um ausgeführt zu werden.

Beispielsweise dauert das Initialisieren und Beenden des Datenbanktests normalerweise einige Zeit.

Wenn Sie Hunderte von Tests haben, benötigen diese aufgrund ihrer Anzahl viel Zeit, um ausgeführt zu werden, auch wenn sie schnell sind.

Was getan werden kann ist:

1) Identifizieren Sie die entscheidenden Tests. Diejenigen für die wichtigsten Teile von Bibliotheken und diejenigen, die nach Änderungen am wahrscheinlichsten versagen. Nur diese Tests sollten immer beim Kompilieren ausgeführt werden. Wenn ein Code häufig fehlerhaft ist, sollten seine Tests obligatorisch sein, auch wenn die Ausführung lange dauert. Wenn andererseits ein Teil der Software nie ein Problem verursacht hat, können Sie Tests für jeden Build sicher überspringen.

2) Bereiten Sie den Continuous Integration Server vor, auf dem alle Tests im Hintergrund ausgeführt werden. Es liegt an Ihnen, ob Sie sich entscheiden, jede Stunde oder nach jedem Commit zu erstellen (das zweite ist nur dann sinnvoll, wenn Sie automatisch erkennen möchten, wessen Commit Probleme verursacht hat).

0
Danubian Sailor

Ich fühle deinen Schmerz und bin auf mehrere Stellen gestoßen, an denen die Build-Geschwindigkeit stark verbessert werden kann. Ich empfehle jedoch, an einem detaillierten Detail zu messen, um herauszufinden, wo Ihr Build am längsten dauert. Zum Beispiel habe ich einen Build mit ungefähr 30 Projekten, dessen Ausführung etwas mehr als eine Minute dauert. Dies ist jedoch nur ein Teil des Bildes. Ich weiß auch, welche Projekte am längsten dauern, was mir hilft, meine Bemühungen zu konzentrieren.

Dinge, die die Bauzeit verschlingen:

  • Herunterladen von Paketen (Nuget für C #, Maven für Java, Gem für Ruby usw.)
  • Kopieren großer Mengen von Dateien in das Dateisystem (Beispiel: GDAL-Unterstützungsdateien)
  • Öffnen von Verbindungen zur Datenbank (einige benötigen mehr als eine Sekunde pro Verbindung, um zu verhandeln)
  • Reflexionsbasierter Code
  • Automatisch generierter Code
  • Verwenden von Ausnahmen zur Steuerung des Programmflusses

Mock-Bibliotheken verwenden entweder Reflection oder injizieren Code mithilfe von Bytecode-Bibliotheken, um den Mock für Sie zu generieren. Es ist zwar sehr praktisch, nimmt aber Testzeit in Anspruch. Wenn Sie in Ihrem Test Mocks innerhalb einer Schleife generieren, kann dies den Unit-Tests eine messbare Zeitspanne hinzufügen.

Es gibt Möglichkeiten, die Probleme zu beheben:

  • Verschieben von Tests mit einer Datenbank in die Integration (d. H. Nur auf dem CI-Buildserver)
  • Vermeiden Sie es, in Ihren Tests Verspottungen in Schleifen zu erstellen. Vermeiden Sie einfach Schleifen in Ihren Tests insgesamt. In diesem Fall können Sie wahrscheinlich dieselben Ergebnisse mit einem parametrisierten Test erzielen.
  • Überlegen Sie, ob Sie Ihre massive Lösung in separate Lösungen aufteilen möchten

Wenn Ihre Lösung über 100 Projekte enthält, verfügen Sie über eine Kombination aus Bibliothekscode, Tests und Anwendungscode. Jede der Bibliotheken kann eine eigene Lösung mit den dazugehörigen Tests sein. Jet Brains Team City ist ein CI-Build-Server, der gleichzeitig als Nuget-Server fungiert - und ich bin sicher, dass dies nicht der einzige ist. Dies gibt Ihnen die Flexibilität, Bibliotheken, die wahrscheinlich nicht häufig geändert werden, in ihre eigenen Lösungen/Projekte zu verschieben und Nuget zu verwenden, um die Abhängigkeiten für Ihren Anwendungscode aufzulösen. Kleinere Lösungen bedeuten, dass Sie Ihre Änderungen an einer Bibliothek schnell und unkompliziert vornehmen und die Vorteile der Hauptlösung nutzen können.

0
Berin Loritsch