it-swarm.com.de

Ist es normal, so viel, wenn nicht mehr Zeit mit dem Schreiben von Tests zu verbringen als mit tatsächlichem Code?

Ich finde Tests viel schwieriger und schwieriger zu schreiben als den tatsächlichen Code, den sie testen. Es ist nicht ungewöhnlich, dass ich mehr Zeit damit verbringe, den Test zu schreiben als den Code, den er testet.

Ist das normal oder mache ich etwas falsch?

Die Fragen „ Lohnt sich Unit-Test oder testgetriebene Entwicklung? “, „ Wir verbringen mehr Zeit mit der Implementierung von Funktionstests als mit der Implementierung des Systems selbst, ist das normal? “ und In ihren Antworten geht es eher darum, ob sich das Testen lohnt (wie in "Sollten wir das Schreiben von Tests insgesamt überspringen?"). Obwohl ich davon überzeugt bin, dass Tests wichtig sind, frage ich mich, ob es normal ist, mehr Zeit mit Tests zu verbringen als mit tatsächlichem Code, oder ob es nur ich bin.

Gemessen an der Anzahl der Aufrufe, Antworten und Gegenstimmen, die meine Frage erhalten hat, kann ich nur davon ausgehen, dass es sich um ein berechtigtes Anliegen handelt, das in keiner anderen Frage auf der Website behandelt wird.

216
springloaded

Ich erinnere mich aus einem Software-Engineering-Kurs, dass einer ~ 10% der Entwicklungszeit damit verbringt, neuen Code zu schreiben, und der andere 90% Debuggen, Testen und Dokumentieren.

Da Unit-Tests das Debuggen und den Testaufwand in (möglicherweise automatisierbaren) Code erfassen, ist es sinnvoll, dass mehr Aufwand in sie fließt. Die tatsächlich benötigte Zeit sollte nicht viel länger sein als das Debuggen und Testen, das man ohne das Schreiben der Tests durchführen würde.

Schließlich sollten Tests auch als Dokumentation dienen! Man sollte Unit-Tests so schreiben, wie der Code verwendet werden soll; d.h. die Tests (und die Verwendung) sollten einfach sein und die komplizierten Dinge in die Implementierung einbringen.

Wenn Ihre Tests schwer zu schreiben sind, ist der Code, den sie testen, wahrscheinlich schwer zu verwenden!

206
esoterik

Es ist.

Selbst wenn Sie nur Unit-Tests durchführen, ist es nicht ungewöhnlich, dass mehr Code in den Tests enthalten ist als der tatsächlich getestete Code. Daran ist nichts auszusetzen.

Betrachten Sie einen einfachen Code:

public void SayHello(string personName)
{
    if (personName == null) throw new NullArgumentException("personName");

    Console.WriteLine("Hello, {0}!", personName);
}

Was wären die Tests? Hier sind mindestens vier einfache Fälle zu testen:

  1. Der Name der Person lautet null. Wird tatsächlich eine Ausnahme ausgelöst? Das sind mindestens drei Zeilen Testcode, die geschrieben werden müssen.

  2. Der Name der Person ist "Jeff". Erhalten wir als Antwort "Hello, Jeff!"? Das sind vier Zeilen Testcode.

  3. Der Personenname ist eine leere Zeichenfolge. Welchen Output erwarten wir? Was ist die tatsächliche Ausgabe? Nebenfrage: Entspricht es den funktionalen Anforderungen? Das bedeutet weitere vier Codezeilen für den Unit-Test.

  4. Der Personenname ist kurz genug für eine Zeichenfolge, aber zu lang, um mit "Hello, " Und dem Ausrufezeichen kombiniert zu werden. Was passiert? ¹

Dies erfordert viel Testcode. Darüber hinaus erfordern die elementarsten Codeteile häufig Setup-Code, der die für den zu testenden Code benötigten Objekte initialisiert, was häufig auch zum Schreiben von Stubs und Mocks usw. führt.

Wenn das Verhältnis sehr groß ist, können Sie in diesem Fall einige Dinge überprüfen:

  • Gibt es bei den Tests eine Codeduplizierung? Die Tatsache, dass es sich um Testcode handelt, bedeutet nicht, dass der Code zwischen ähnlichen Tests dupliziert (kopiert und eingefügt) werden sollte. Eine solche Duplizierung erschwert die Wartung dieser Tests.

  • Gibt es redundante Tests? Als Faustregel gilt: Wenn Sie einen Komponententest entfernen, sollte sich die Zweigabdeckung verringern. Wenn dies nicht der Fall ist, wird möglicherweise angezeigt, dass der Test nicht benötigt wird, da die Pfade bereits von anderen Tests abgedeckt werden.

  • Testen Sie nur den Code, den Sie testen sollten? Es wird nicht erwartet, dass Sie das zugrunde liegende Framework testen der Bibliotheken von Drittanbietern, sondern ausschließlich den Code des Projekts selbst.

Mit Rauchtests, System- und Integrationstests, Funktions- und Abnahmetests sowie Belastungs- und Belastungstests fügen Sie noch mehr Testcode hinzu, sodass Sie sich keine Sorgen machen sollten, wenn Sie vier oder fünf LOC-Tests für jeden LOC des tatsächlichen Codes haben.

Ein Hinweis zu TDD

Wenn Sie sich Sorgen über die Zeit machen, die zum Testen Ihres Codes benötigt wird, kann es sein, dass Sie es falsch machen, dh zuerst Code, später Tests. In diesem Fall kann TDD helfen, indem es Sie dazu ermutigt, in Iterationen von 15 bis 45 Sekunden zu arbeiten und zwischen Code und Tests zu wechseln. Laut den Befürwortern von TDD beschleunigt es den Entwicklungsprozess, indem es sowohl die Anzahl der durchzuführenden Tests als auch, was noch wichtiger ist, die Menge des zu schreibenden Geschäftscodes reduziert und insbesondere neu schreibt zum Testen.


¹ Sei n die maximale Länge eines Strings . Wir können SayHello aufrufen und als Referenz eine Zeichenfolge der Länge n - 1 übergeben, die gut funktionieren sollte. Im Schritt Console.WriteLine Sollte die Formatierung mit einer Zeichenfolge der Länge n + 8 enden, was zu einer Ausnahme führt. Möglicherweise führt aufgrund der Speicherbeschränkungen sogar eine Zeichenfolge mit n /2 Zeichen zu einer Ausnahme. Die Frage, die man sich stellen sollte, ist, ob dieser vierte Test ein Komponententest ist (er sieht aus wie einer, kann aber im Vergleich zu durchschnittlichen Komponententests einen viel höheren Einfluss auf die Ressourcen haben) und ob er den tatsächlichen Code oder das zugrunde liegende Framework testet.

97

Ich denke, es ist wichtig, zwischen zwei Arten von Teststrategien zu unterscheiden: Unit-Tests und Integrations-/Abnahmetests.

Obwohl in einigen Fällen Unit-Tests erforderlich sind, werden diese häufig hoffnungslos übertrieben. Dies wird durch bedeutungslose Metriken verschärft, die Entwicklern aufgezwungen werden, wie "100% Abdeckung". http://www.rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf liefert hierfür ein überzeugendes Argument. Berücksichtigen Sie beim aggressiven Testen von Einheiten die folgenden Probleme:

  • Eine Fülle sinnloser Tests, die keinen Geschäftswert dokumentieren, sondern nur existieren, um dieser 100% igen Abdeckung näher zu kommen. Wo ich arbeite, müssen wir Unit-Tests für Fabriken schreiben, die nichts anderes tun, als neue Instanzen von Klassen zu erstellen. Es bringt keinen Wert. Oder diese langwierigen .equals () -Methoden, die von Eclipse generiert wurden - diese müssen nicht getestet werden.
  • Um das Testen zu vereinfachen, würden Entwickler komplexe Algorithmen in kleinere testbare Einheiten unterteilen. Klingt nach einem Gewinn, oder? Nicht, wenn 12 Klassen geöffnet sein müssen, um einem gemeinsamen Codepfad zu folgen. In diesen Fällen kann das Testen von Einheiten die Lesbarkeit des Codes tatsächlich beeinträchtigen. Ein weiteres Problem dabei ist, dass Sie, wenn Sie Ihren Code in zu kleine Teile zerlegen, eine Vielzahl von Klassen (oder Codeteilen) erhalten, die keine Rechtfertigung zu haben scheinen, außer eine Teilmenge eines anderen Codeteils zu sein.
  • Das Refactoring von Code mit hoher Abdeckung kann schwierig sein, da Sie auch die Fülle von Unit-Tests beibehalten müssen, die davon abhängen, dass sie funktionieren , nur damit . Dies wird durch Verhaltens-Unit-Tests verschärft, bei denen ein Teil Ihres Tests auch die Interaktion der Mitarbeiter einer Klasse überprüft (normalerweise verspottet).

Integrations-/Akzeptanztests sind dagegen ein äußerst wichtiger Bestandteil der Softwarequalität, und meiner Erfahrung nach sollten Sie viel Zeit darauf verwenden, sie richtig zu machen.

Viele Geschäfte haben die TDD-Kool-Hilfe getrunken, aber wie der obige Link zeigt, zeigen eine Reihe von Studien, dass ihr Nutzen nicht schlüssig ist.

61
firtydank

Kann nicht verallgemeinert werden.

Wenn ich eine Formel oder einen Algorithmus aus physikalisch basiertem Rendering implementieren muss, kann es durchaus sein, dass ich 10 Stunden mit paranoiden Komponententests verbringe, da ich weiß, dass der kleinste Fehler oder die Ungenauigkeit Monate später zu Fehlern führen kann, die fast unmöglich zu diagnostizieren sind .

Wenn ich nur einige Codezeilen logisch gruppieren und ihm einen Namen geben möchte, der nur im Dateibereich verwendet wird, kann ich ihn möglicherweise überhaupt nicht testen (wenn Sie ausnahmslos darauf bestehen, Tests für jede einzelne Funktion zu schreiben, können Programmierer zurückgreifen so wenig Funktionen wie möglich zu schreiben).

12
phresnel

Ich finde es der wichtigste Teil.

Beim Unit-Test geht es nicht immer darum, "zu sehen, ob es richtig funktioniert", sondern darum, zu lernen. Sobald Sie etwas genug getestet haben, wird es in Ihrem Gehirn "fest codiert" und Sie verkürzen schließlich Ihre Zeit für Unit-Tests und können feststellen, dass Sie ganze Klassen und Methoden schreiben, ohne etwas zu testen, bis Sie fertig sind.

Aus diesem Grund wird in einer der anderen Antworten auf dieser Seite erwähnt, dass in einem "Kurs" 90% der Tests durchgeführt wurden, da jeder die Vorbehalte seines Ziels kennenlernen musste.

Unit-Tests sind nicht nur eine sehr wertvolle Nutzung Ihrer Zeit, da sie Ihre Fähigkeiten buchstäblich verbessern, sondern auch eine gute Möglichkeit sind, Ihren eigenen Code erneut zu überprüfen und dabei einen logischen Fehler zu finden.

4
Jesse

Ja, es ist normal, wenn Sie über TDDing sprechen. Wenn Sie automatisierte Tests haben, sichern Sie das gewünschte Verhalten Ihres Codes. Wenn Sie Ihre Tests zuerst schreiben, stellen Sie fest, ob vorhandener Code bereits das gewünschte Verhalten aufweist.

Das bedeutet, dass:

  • Wenn Sie einen fehlgeschlagenen Test schreiben, ist die Korrektur des Codes mit der einfachsten Funktion kürzer als das Schreiben des Tests.
  • Wenn Sie einen Test schreiben, der erfolgreich ist, müssen Sie keinen zusätzlichen Code schreiben, sodass Sie effektiv mehr Zeit für das Schreiben von Tests als für Code benötigen.

(Dies berücksichtigt nicht das Code-Refactoring, bei dem weniger Zeit für das Schreiben von nachfolgendem Code aufgewendet werden soll. Dies wird durch das Test-Refactoring ausgeglichen, bei dem weniger Zeit für das Schreiben von nachfolgenden Tests aufgewendet werden soll.)

Ja, wenn Sie nachträglich über das Schreiben von Tests sprechen, werden Sie mehr Zeit verbringen:

  • Bestimmtes Verhalten bestimmen.
  • Bestimmen von Möglichkeiten zum Testen des gewünschten Verhaltens.
  • Code-Abhängigkeiten erfüllen, um die Tests schreiben zu können.
  • Korrigieren des Codes für fehlgeschlagene Tests.

Dann werden Sie tatsächlich Code schreiben.

Also ja, es ist eine erwartete Maßnahme.

3

Ist es normal, so viel, wenn nicht mehr Zeit mit dem Schreiben von Tests zu verbringen als mit tatsächlichem Code?

Ja, so ist es. Mit einigen Einschränkungen.

Erstens ist es "normal" in dem Sinne, dass die meisten großen Geschäfte auf diese Weise funktionieren. Selbst wenn dies völlig falsch und dumm war, macht es die Tatsache, dass die meisten großen Geschäfte auf diese Weise funktionieren, "normal".

Damit meine ich nicht, dass das Testen falsch ist. Ich habe in Umgebungen ohne Tests und in Umgebungen mit Zwangstests gearbeitet, und ich kann Ihnen immer noch sagen, dass selbst das Zwangstest besser war als kein Test.

Und ich mache noch kein TDD (wer weiß, ich könnte es in Zukunft tun), aber ich mache die überwiegende Mehrheit meiner Edit-Run-Debug-Zyklen, indem ich die Tests ausführe, nicht die eigentliche Anwendung, also arbeite ich natürlich viel bei meinen Tests, um zu vermeiden, dass die eigentliche Anwendung so weit wie möglich ausgeführt werden muss.

Beachten Sie jedoch, dass bei übermäßigen Tests Gefahren bestehen, insbesondere bei der Zeit, die für die Wartung der Tests aufgewendet wird. (Ich schreibe diese Antwort hauptsächlich, um ausdrücklich darauf hinzuweisen.)

Im Vorwort von Roy Osheroves The Art of Unit Testing (Manning, 2009) der Autor gibt zu, an einem Projekt teilgenommen zu haben, das fehlgeschlagen ist Ein großer Teil davon ist auf die enorme Entwicklungslast zurückzuführen, die durch schlecht konzipierte Komponententests verursacht wird, die während der gesamten Dauer des Entwicklungsaufwands beibehalten werden mussten. Wenn Sie also zu viel Zeit damit verbringen, nur Ihre Tests zu warten, ist dies der Fall bedeutet nicht unbedingt, dass Sie auf dem richtigen Weg sind, denn es ist "normal". Ihre Entwicklungsanstrengungen sind möglicherweise in einen ungesunden Modus geraten, in dem möglicherweise ein radikales Überdenken Ihrer Testmethode erforderlich ist, um das Projekt zu speichern.

2
Mike Nakis

Es kann für viele Menschen sein, aber es kommt darauf an.

Wenn Sie zuerst Tests (TDD) schreiben, kann es zu Überschneidungen in der Zeit kommen, die Sie für das Schreiben des Tests aufgewendet haben. Dies ist tatsächlich hilfreich für das Schreiben des Codes. Erwägen:

  • Ermittlung von Ergebnissen und Eingaben (Parametern)
  • Regeln der Namensgebung
  • Struktur - wo man Dinge hinstellt.
  • Einfaches altes Denken

Wenn Sie Tests nach dem Schreiben des Codes schreiben, stellen Sie möglicherweise fest, dass Ihr Code nicht leicht zu testen ist, sodass das Schreiben von Tests schwieriger ist/länger dauert.

Die meisten Programmierer haben viel länger Code geschrieben als Tests, daher würde ich erwarten, dass die meisten von ihnen nicht so fließend sind. Außerdem können Sie die Zeit hinzufügen, die erforderlich ist, um Ihr Testframework zu verstehen und zu verwenden.

Ich denke, wir müssen unsere Einstellung dahingehend ändern, wie lange das Codieren dauert und wie Unit-Tests durchgeführt werden. Betrachten Sie es niemals kurzfristig und vergleichen Sie niemals nur die Gesamtzeit, um eine bestimmte Funktion bereitzustellen, da Sie berücksichtigen müssen, dass Sie nicht nur besseren/weniger fehlerhaften Code schreiben, sondern auch Code, der einfacher zu ändern ist und dennoch erstellt wird besser/weniger fehlerhaft.

Irgendwann sind wir alle nur in der Lage, Code zu schreiben, der so gut ist, dass einige der Tools und Techniken nur so viel zur Verbesserung unserer Fähigkeiten beitragen können. Es ist nicht so, dass ich ein Haus bauen könnte, wenn ich nur eine lasergeführte Säge hätte.

2
JeffO

Ist es normal, so viel, wenn nicht mehr Zeit mit dem Schreiben von Tests zu verbringen als mit tatsächlichem Code?

  • Ja zum Schreiben nit-Tests (Test eines Moduls isoliert) wenn der Code stark gekoppelt (Legacy, nicht genug - Trennung von Bedenken , fehlt Abhängigkeitsinjektion , nicht tdd entwickelt )
  • Ja zum Schreiben Integrations-/Abnahmetests wenn die zu testende Logik nur über GUI-Code erreichbar ist
  • Nein zum Schreiben von Integrations-/Abnahmetests, solange der GUI-Code und die Geschäftslogik getrennt sind (Der Test muss nicht mit dem interagieren gui)
  • Nein zum Schreiben von Unit-Tests, wenn es Bedenken gibt, Abhängigkeitsinjektion, Code wurde testgetrieben entwickelt (tdd)
1
k3b