it-swarm.com.de

Wie tief sind Ihre Unit-Tests?

Ich habe festgestellt, dass TDD einige Zeit in Anspruch nimmt, um Ihre Tests einzurichten, und weil ich natürlich faul bin, möchte ich immer so wenig Code wie möglich schreiben. Das erste, was ich zu tun scheine, ist zu testen, ob mein Konstruktor alle Eigenschaften festgelegt hat, aber ist das übertrieben?

Meine Frage ist, auf welcher Granularitätsstufe schreiben Sie Ihre Unit-Tests?

..und gibt es einen Fall von zu viel testen?

88
Johnno Nolan

Ich werde für Code bezahlt, der funktioniert, nicht für Tests. Daher ist es meine Philosophie, so wenig wie möglich zu testen, um ein bestimmtes Vertrauensniveau zu erreichen. . Wenn ich normalerweise keinen Fehler mache (wie das Setzen der falschen Variablen in einem Konstruktor), teste ich nicht darauf. Ich neige dazu, Testfehler zu verstehen, daher bin ich besonders vorsichtig, wenn ich Logik mit komplizierten Bedingungen habe. Wenn ich in einem Team programmiere, ändere ich meine Strategie, um den Code sorgfältig zu testen, bei dem wir uns im Allgemeinen irren.

Verschiedene Leute werden unterschiedliche Teststrategien haben, die auf dieser Philosophie basieren, aber das erscheint mir angesichts des unausgereiften Verständnisses, wie Tests am besten in die innere Schleife der Codierung passen, vernünftig. In zehn oder zwanzig Jahren werden wir wahrscheinlich eine allgemeinere Theorie haben, welche Tests zu schreiben sind, welche nicht zu schreiben sind und wie man den Unterschied erkennt. In der Zwischenzeit scheint das Experimentieren in Ordnung zu sein.

221
Kent Beck

Schreiben Sie Komponententests für Dinge, von denen Sie erwarten, dass sie brechen, und für Edge-Fälle. Danach sollten Testfälle hinzugefügt werden, sobald Fehlerberichte eingehen - bevor das Update für den Fehler geschrieben wird. Der Entwickler kann dann sicher sein, dass:

  1. Der Fehler ist behoben;
  2. Der Bug wird nicht wieder auftauchen.

Laut Kommentar im Anhang kann dieser Ansatz zum Schreiben von Komponententests zu Problemen führen, wenn im Laufe der Zeit viele Fehler in einer bestimmten Klasse entdeckt werden. Dies ist wahrscheinlich der Punkt, an dem Diskretion hilfreich ist - das Hinzufügen von Komponententests nur für Fehler, bei denen ein erneutes Auftreten wahrscheinlich ist oder bei denen ein erneutes Auftreten schwerwiegende Probleme verursachen würde. Ich habe festgestellt, dass in diesen Szenarien ein gewisses Maß an Integrationstests in Komponententests hilfreich sein kann - Codepfade mit höherem Wert können Codepfade mit niedrigerem Wert abdecken.

20
Dominic Rodger

Alles sollte so einfach wie möglich gemacht werden, aber nicht einfacher. - A. Einstein

Eines der am meisten missverstandenen Dinge über TDD ist das erste Wort darin. Prüfung. Deshalb ist BDD mitgekommen. Weil die Leute nicht wirklich verstanden haben, dass das erste D das wichtige war, nämlich Driven. Wir alle neigen dazu, ein bisschen zu viel über das Testen und ein bisschen zu wenig über das Fahren des Designs nachzudenken. Und ich denke, dass dies eine vage Antwort auf Ihre Frage ist, aber Sie sollten sich wahrscheinlich überlegen, wie Sie Ihren Code fahren, anstatt was Sie tatsächlich testen. Ein Coverage-Tool kann Ihnen dabei helfen. Design ist ein viel größeres und problematischeres Thema.

19
kitofr

Für diejenigen, die "alles" testen möchten: stellen Sie fest, dass zum "vollständigen Testen" einer Methode wie int square(int x) ungefähr 4 Milliarden Testfälle in gängigen Sprachen und typischen Umgebungen erforderlich sind.

Tatsächlich ist es sogar noch schlimmer: Eine Methode void setX(int newX) muss auch not ​​die Werte aller anderen Member außer x ändern - testen Sie das? obj.y, obj.z Usw. bleiben nach dem Aufruf von obj.setX(42); unverändert.

Es ist nur praktisch, eine Teilmenge von "alles" zu testen. Sobald Sie dies akzeptieren, wird es schmackhafter, zu erwägen, nicht unglaublich grundlegendes Verhalten zu testen. Jeder Programmierer hat eine Wahrscheinlichkeitsverteilung von Fehlerstellen; Der intelligente Ansatz besteht darin, Ihre Energie auf Testregionen zu konzentrieren, in denen Sie die Fehlerwahrscheinlichkeit als hoch einschätzen.

15
j_random_hacker

Die klassische Antwort lautet "Teste alles, was kaputt gehen könnte". Ich interpretiere das so, dass das Testen von Setzern und Gettern, die nichts anderes als Setzen oder Erhalten tun, wahrscheinlich zu viel Testen ist, ohne dass Sie sich die Zeit nehmen müssen. Wenn Ihr IDE diese nicht für Sie schreibt, können Sie dies auch tun.

Wenn Ihr Konstruktor nicht das Festlegen von Eigenschaften zu einem späteren Zeitpunkt zu Fehlern führen könnte, ist das Testen, ob sie festgelegt sind, kein Overkill.

9
Dennis S.

Ich schreibe Tests, um die Annahmen der Klassen abzudecken, die ich schreiben werde. Die Tests setzen die Anforderungen durch. Wenn x beispielsweise niemals 3 sein kann, stelle ich sicher, dass es einen Test gibt, der diese Anforderung abdeckt.

Wenn ich keinen Test schreibe, der einen Zustand abdeckt, taucht er immer später beim "menschlichen" Testen auf. Dann schreibe ich bestimmt eine, aber ich fange sie lieber früh auf. Ich denke, der Punkt ist, dass das Testen (vielleicht) langwierig, aber notwendig ist. Ich schreibe genug Tests, um vollständig zu sein, aber nicht mehr.

5
itsmatt

Ein Teil des Problems beim Überspringen einfacher Tests besteht jetzt darin, dass das zukünftige Refactoring diese einfache Eigenschaft mit viel Logik sehr kompliziert machen könnte. Ich denke, die beste Idee ist, dass Sie Tests verwenden können, um die Anforderungen für das Modul zu überprüfen. Wenn Sie beim Bestehen von X Y zurückbekommen sollten, ist dies das, was Sie testen möchten. Wenn Sie den Code später ändern, können Sie überprüfen, ob X für Sie Y ergibt, und Sie können einen Test für A für B hinzufügen, wenn diese Anforderung später hinzugefügt wird.

Ich habe festgestellt, dass sich die Zeit, die ich beim Schreiben von Tests für die erste Entwicklung aufgewendet habe, in der ersten oder zweiten Fehlerbehebung auszahlt. Die Fähigkeit, Code zu finden, den Sie seit 3 ​​Monaten nicht mehr gesehen haben, und sicher zu sein, dass Ihr Fix alle Fälle abdeckt und "wahrscheinlich" nichts kaputt macht, ist äußerst wertvoll. Sie werden auch feststellen, dass Unit-Tests dazu beitragen, Fehler weit über den Stack-Trace hinaus zu untersuchen. Wenn Sie sehen, wie einzelne Teile der App funktionieren und wie sie versagen, erhalten Sie einen umfassenden Einblick, warum sie funktionieren oder als Ganzes versagen.

5
Matt

In den meisten Fällen würde ich sagen, wenn es dort Logik gibt, testen Sie sie. Dies schließt Konstruktoren und Eigenschaften ein, insbesondere, wenn mehr als eine Eigenschaft in der Eigenschaft festgelegt wird.

In Bezug auf zu viele Tests ist es umstritten. Einige würden sagen, dass alles auf Robustheit getestet werden sollte, andere sagen, dass für ein effizientes Testen nur Dinge getestet werden sollten, die brechen könnten (d. H. Logik).

Ich würde mich mehr auf das zweite Lager konzentrieren, nur aus persönlicher Erfahrung, aber wenn sich jemand entschließen würde, alles zu testen, würde ich nicht sagen, dass es zu viel ist ... vielleicht ein bisschen übertrieben für mich, aber nicht zu viel für sie.

Also, nein - ich würde sagen, es gibt nicht so etwas wie "zu viel" Testen im allgemeinen Sinne, nur für Einzelpersonen.

4
Fry

Testgesteuerte Entwicklung bedeutet, dass Sie die Codierung beenden, wenn alle Ihre Tests erfolgreich abgeschlossen wurden.

Wenn Sie keinen Test für eine Eigenschaft haben, warum sollten Sie ihn dann implementieren? Was soll die Eigenschaft tun, wenn Sie das erwartete Verhalten im Falle einer "illegalen" Zuweisung nicht testen/definieren?

Deshalb bin ich total dafür, jedes Verhalten zu testen, das eine Klasse zeigen sollte. Einschließlich "primitiver" Eigenschaften.

Um diesen Test zu vereinfachen, habe ich eine einfache NUnit TestFixture erstellt, die Erweiterungspunkte zum Festlegen/Abrufen des Werts bereitstellt, Listen gültiger und ungültiger Werte erstellt und mit einem einzigen Test überprüft, ob die Eigenschaft ordnungsgemäß funktioniert. Das Testen einer einzelnen Eigenschaft könnte folgendermaßen aussehen:

[TestFixture]
public class Test_MyObject_SomeProperty : PropertyTest<int>
{

    private MyObject obj = null;

    public override void SetUp() { obj = new MyObject(); }
    public override void TearDown() { obj = null; }

    public override int Get() { return obj.SomeProperty; }
    public override Set(int value) { obj.SomeProperty = value; }

    public override IEnumerable<int> SomeValidValues() { return new List() { 1,3,5,7 }; }
    public override IEnumerable<int> SomeInvalidValues() { return new List() { 2,4,6 }; }

}

Mit Lambdas und Attributen könnte dies sogar kompakter geschrieben werden. Ich nehme an, MBUnit hat sogar native Unterstützung für solche Dinge. Der Punkt ist jedoch, dass der obige Code die Absicht der Eigenschaft erfasst.

P .: Wahrscheinlich sollte der PropertyTest auch eine Möglichkeit bieten, zu überprüfen, ob sich other Eigenschaften am Objekt nicht geändert haben. Hmm .. zurück zum Zeichenbrett.

3
David Schmitt

Ich mache einen Unit-Test, um die maximal mögliche Abdeckung zu erreichen. Wenn ich keinen Code erreiche, refaktoriere ich, bis die Abdeckung so vollständig wie möglich ist

Nach Abschluss des Blinding-Schreibtests schreibe ich normalerweise einen Testfall, der jeden Fehler reproduziert

Ich bin es gewohnt, zwischen Codetests und Integrationstests zu unterscheiden. Während der Integrationstests (die auch Komponententests sind, aber Gruppen von Komponenten betreffen, also nicht genau das, wofür Komponententests gedacht sind) werde ich testen, ob die Anforderungen korrekt implementiert werden.

1

Je mehr ich meine Programmierung durch das Schreiben von Tests vorantreibe, desto weniger mache ich mir Sorgen über die Granualität der Tests. Rückblickend scheint es so, als ob ich das Einfachste tue, um mein Ziel der Validierung zu erreichen Verhalten. Dies bedeutet, dass ich ein gewisses Maß an Vertrauen erzeuge, dass mein Code das tut, was ich von ihm verlange. Dies gilt jedoch nicht als absolute Garantie dafür, dass mein Code fehlerfrei ist. Ich bin der Meinung, dass die richtige Balance darin besteht, das Standardverhalten zu testen, und vielleicht ein oder zwei Edge-Fälle, um dann mit dem nächsten Teil meines Designs fortzufahren.

Ich akzeptiere, dass dies nicht alle Fehler abdeckt und verwende andere traditionelle Testmethoden, um diese zu erfassen.

1
Johnno Nolan

Diese Antwort dient eher dazu, herauszufinden, wie viele Komponententests für eine bestimmte Methode verwendet werden sollen, von der Sie wissen, dass sie aufgrund ihrer Kritikalität/Wichtigkeit getestet werden soll. Mit der Basis-Path-Testing-Technik von McCabe können Sie Folgendes tun, um quantitativ eine bessere Codeabdeckungssicherheit zu erzielen als mit einfacher "Anweisungsabdeckung" oder "Zweigabdeckung":

  1. Ermitteln Sie den Wert für die zyklomatische Komplexität Ihrer Methode, die Sie einem Komponententest unterziehen möchten (Visual Studio 2010 Ultimate kann dies beispielsweise mit statischen Analysetools für Sie berechnen; andernfalls können Sie ihn manuell über die Flowgraph-Methode berechnen - http: // users.csc.calpoly.edu/~jdalbey/206/Lectures/BasisPathTutorial/index.html )
  2. Listen Sie den Basissatz unabhängiger Pfade auf, die durch Ihre Methode fließen - siehe Link oben für ein Flussdiagrammbeispiel
  3. Bereiten Sie Unit-Tests für jeden in Schritt 2 bestimmten unabhängigen Basispfad vor
0
J.D.

Testen Sie den Quellcode, der Sie beunruhigt.

Es ist nicht nützlich, Codeteile zu testen, mit denen Sie sehr sicher sind, solange Sie keine Fehler machen.

Testen Sie Bugfixes, damit Sie einen Fehler zum ersten und letzten Mal beheben.

Testen Sie, um das Vertrauen in undurchsichtige Codeabschnitte zu erlangen, damit Sie Wissen erstellen.

Testen Sie vor dem schweren und mittleren Refactoring, damit Sie vorhandene Funktionen nicht beschädigen.

0
castle1971

Im Allgemeinen beginne ich klein, mit Ein- und Ausgängen, von denen ich weiß, dass sie funktionieren müssen. Wenn ich dann Fehler behebe, füge ich weitere Tests hinzu, um sicherzustellen, dass die von mir behobenen Probleme getestet werden. Es ist organisch und funktioniert gut für mich.

Kannst du zu viel testen? Wahrscheinlich, aber es ist wahrscheinlich besser, generell auf Nummer sicher zu gehen, auch wenn dies davon abhängt, wie wichtig Ihre Anwendung ist.

0
Tim Sullivan

Je mehr ich darüber lese, desto mehr denke ich, dass einige Komponententests wie Muster aussehen: Ein Geruch von unzureichenden Sprachen.

Wenn Sie testen müssen, ob Ihr Trivial-Getter tatsächlich den richtigen Wert zurückgibt, können Sie den Getter-Namen und den Namen der Member-Variablen miteinander mischen. Geben Sie 'attr_reader: name' von Ruby ein, und das kann nicht mehr passieren. Nur in Java nicht möglich.

Wenn Ihr Getter jemals nicht mehr trivial wird, können Sie dann noch einen Test hinzufügen.

0
Andreas Krey

Ich denke, Sie müssen alles in Ihrem "Kern" Ihrer Geschäftslogik testen. Getter und Setter auch, weil sie einen negativen Wert oder einen Nullwert akzeptieren könnten, den Sie möglicherweise nicht akzeptieren möchten. Wenn Sie Zeit haben (immer von Ihrem Chef abhängig), ist es gut, andere Geschäftslogik und alle Controller, die dieses Objekt aufrufen, zu testen (Sie wechseln langsam vom Komponententest zum Integrationstest).

0

Ich teste keine einfachen Setter/Getter-Methoden, die keine Nebenwirkungen haben. Aber ich teste jede andere öffentliche Methode. Ich versuche, Tests für alle Randbedingungen in meinen Algorthims zu erstellen und die Abdeckung meiner Unit-Tests zu überprüfen.

Es ist viel Arbeit, aber ich denke, es lohnt sich. Ich würde lieber Code schreiben (sogar Code testen), als Code in einem Debugger schrittweise durchzugehen. Ich finde den Code-Build-Deploy-Debug-Zyklus sehr zeitaufwendig und je ausführlicher die in meinen Build integrierten Komponententests sind, desto weniger Zeit verbringe ich mit dem Code-Build-Deploy-Debug-Zyklus.

Sie haben nicht gesagt, warum Sie auch Architektur programmieren. Aber für Java Ich benutze Maven 2 , JUnit , DbUnit , Cobertura , & EasyMock .

0
Brian Matthews