Wie testet man einen Unit-Test? [geschlossen]


89

Ich habe Rob Connerys Webcasts in der MVCStoreFront-App angesehen und festgestellt, dass er selbst die alltäglichsten Dinge wie:

public Decimal DiscountPrice
{
   get
   {
       return this.Price - this.Discount;
   }
}

Hätte einen Test wie:

[TestMethod]
public void Test_DiscountPrice
{
    Product p = new Product();
    p.Price = 100;
    p.Discount = 20;
    Assert.IsEqual(p.DiscountPrice,80);
}

Ich bin zwar alle für Unit-Tests, aber manchmal frage ich mich, ob diese Form der ersten Testentwicklung wirklich von Vorteil ist. In einem realen Prozess befinden sich beispielsweise 3-4 Ebenen über Ihrem Code (Geschäftsanforderung, Anforderungsdokument, Architekturdokument). , wo die tatsächlich definierte Geschäftsregel (Rabattpreis ist Preis - Rabatt) falsch definiert werden könnte.

In diesem Fall bedeutet Ihnen Ihr Komponententest nichts.

Darüber hinaus ist Ihr Komponententest ein weiterer Fehlerpunkt:

[TestMethod]
public void Test_DiscountPrice
{
    Product p = new Product();
    p.Price = 100;
    p.Discount = 20;
    Assert.IsEqual(p.DiscountPrice,90);
}

Jetzt ist der Test fehlerhaft. In einem einfachen Test ist das natürlich keine große Sache, aber sagen wir, wir haben eine komplizierte Geschäftsregel getestet. Was gewinnen wir hier?

Schneller Vorlauf zwei Jahre in der Lebensdauer der Anwendung, wenn Wartungsentwickler sie warten. Jetzt ändert das Unternehmen seine Regel und der Test bricht erneut ab. Ein Rookie-Entwickler behebt den Test dann falsch. Wir haben jetzt einen weiteren Fehlerpunkt.

Alles, was ich sehe, sind mehr mögliche Fehlerquellen ohne wirklich vorteilhafte Rendite. Wenn der Rabattpreis falsch ist, wird das Testteam immer noch das Problem finden. Wie haben Unit-Tests Arbeit gespart?

Was vermisse ich hier? Bitte bringen Sie mir bei, TDD zu lieben, da es mir bisher schwer fällt, es als nützlich zu akzeptieren. Ich will auch, weil ich progressiv bleiben will, aber es macht für mich einfach keinen Sinn.

EDIT: Ein paar Leute erwähnen immer wieder, dass Tests helfen, die Spezifikation durchzusetzen. Ich habe die Erfahrung gemacht, dass die Spezifikation meistens auch falsch war, aber vielleicht bin ich dazu verdammt, in einer Organisation zu arbeiten, in der die Spezifikationen von Leuten geschrieben werden, die keine Spezifikationen schreiben sollten.


5
In vielen Fällen ist der Komponententest die Spezifikation und auch die Dokumentation!
Steven A. Lowe

32
... und dann Unit-Test den Unit-Test des Unit-Tests ... aber was ist mit dem Unit-4-Test und dem Unit-5-Test ... aaaaaaaahhhhhhhh!
Dacracot

14
Keine Menge von Tests wird Sie vor einer falschen Spezifikation bewahren.
Bill the Lizard

4
Hat noch jemand gehört, was sich anhörte, als würde eine Hand klatschen?
Gnovice

9
Ich denke, das passende Zitat lautet: "Es sind nur Schildkröten ganz unten."
Quinn Taylor

Antworten:


63

Erstens ist Testen wie Sicherheit - Sie können nie 100% sicher sein, dass Sie es haben, aber jede Schicht bietet mehr Vertrauen und einen Rahmen, um die verbleibenden Probleme einfacher zu beheben.

Zweitens können Sie Tests in Unterprogramme aufteilen, die dann selbst getestet werden können. Wenn Sie 20 ähnliche Tests haben, bedeutet das Erstellen einer (getesteten) Unterroutine, dass Ihr Haupttest 20 einfache Aufrufe der Unterroutine sind, was mit größerer Wahrscheinlichkeit korrekt ist.

Drittens würden einige argumentieren, dass TDD dieses Problem angeht. Das heißt, wenn Sie nur 20 Tests schreiben und diese bestehen, sind Sie nicht ganz sicher, dass sie tatsächlich etwas testen. Aber wenn jeder Test, den Sie ursprünglich geschrieben haben, fehlgeschlagen ist und Sie ihn dann behoben haben, sind Sie viel sicherer, dass er Ihren Code wirklich testet. Meiner Meinung nach dauert dieses Hin und Her mehr Zeit als es wert ist, aber es ist ein Prozess, der versucht, Ihr Anliegen anzusprechen.


2
Um Devils Advocate zu spielen, sehe ich zusätzliche Ebenen als mögliche Fehlerquellen, die das Vertrauen für mich nicht erhöhen. In meinem eigentlichen Job arbeite ich mit vielen Teams über ein stark verteiltes SOA-Unternehmen. Jedes dieser Teams könnte das Projekt gefährden, wenn ihre Schicht ausfällt.
FlySwat

2
Aus diesem Grund verwenden Sie Scheinobjekte, um jede Ebene einzeln zu testen.
Toon Krijthe

10
Großartig, also wird mein Test mit IMockedComplexObject bestanden, aber wenn ich tatsächlich ein ComplexObject in der realen Welt verwende, schlägt es fehl ... Ich habe wieder nichts gewonnen.
FlySwat

16
@Jonathan - nein, Sie haben das Vertrauen gewonnen, dass Ihr Code funktioniert - vorausgesetzt, Sie haben sich zur Schnittstelle von ComplexObject entwickelt und angemessen gegen diese Schnittstelle getestet. Im schlimmsten Fall haben Sie das Wissen gewonnen, dass Ihr Verständnis von ComplexObject nicht Ihren Erwartungen entsprach.
Tvanfosson

9
@FlySwat: Als Antwort auf Ihren Kommentar zu IMockedComplexObject zitiere ich Cwashs Kommentar zu einer anderen Antwort: "Sie verspotten nicht, was Sie testen möchten, Sie verspotten, was Sie nicht testen möchten."
Brian

39

Es ist unwahrscheinlich, dass ein falscher Test Ihren Produktionscode beschädigt. Zumindest nicht schlimmer als überhaupt keinen Test zu haben. Es handelt sich also nicht um einen "Fehlerpunkt": Die Tests müssen nicht korrekt sein, damit das Produkt tatsächlich funktioniert. Sie müssen möglicherweise korrekt sein, bevor sie als funktionsfähig abgemeldet werden, aber das Beheben fehlerhafter Tests gefährdet Ihren Implementierungscode nicht.

Sie können sich Tests, selbst triviale Tests wie diese, als eine zweite Meinung vorstellen, was der Code tun soll. Eine Meinung ist der Test, die andere ist die Implementierung. Wenn sie nicht übereinstimmen, wissen Sie, dass Sie ein Problem haben und schauen genauer hin.

Es ist auch nützlich, wenn jemand in Zukunft dieselbe Schnittstelle von Grund auf neu implementieren möchte. Sie sollten nicht die erste Implementierung lesen müssen, um zu wissen, was Rabatt bedeutet, und die Tests dienen als eindeutige Sicherung für jede schriftliche Beschreibung der Schnittstelle, die Sie möglicherweise haben.

Das heißt, Sie tauschen Zeit aus. Wenn es andere Tests gibt, die Sie mit der Zeit schreiben könnten, die Sie sparen, um diese trivialen Tests zu überspringen, wären sie möglicherweise wertvoller. Das hängt wirklich von Ihrem Testaufbau und der Art der Anwendung ab. Wenn der Rabatt für die App wichtig ist, werden Sie bei Funktionstests ohnehin Fehler in dieser Methode feststellen. Mit allen Unit-Tests können Sie sie an dem Punkt abfangen, an dem Sie dieses Gerät testen, wenn der Ort des Fehlers sofort ersichtlich ist, anstatt zu warten, bis die App zusammen integriert ist und der Ort des Fehlers möglicherweise weniger offensichtlich ist.

Übrigens würde ich persönlich im Testfall nicht 100 als Preis verwenden (oder besser gesagt, wenn ich das tun würde, würde ich einen weiteren Test mit einem anderen Preis hinzufügen). Der Grund ist, dass jemand in Zukunft denken könnte, dass der Rabatt ein Prozentsatz sein soll. Ein Zweck solcher trivialer Tests besteht darin, sicherzustellen, dass Fehler beim Lesen der Spezifikation korrigiert werden.

[In Bezug auf die Bearbeitung: Ich denke, es ist unvermeidlich, dass eine falsche Spezifikation ein Fehlerpunkt ist. Wenn Sie nicht wissen, was die App tun soll, wird sie es wahrscheinlich nicht tun. Das Schreiben von Tests, die die Spezifikation widerspiegeln, vergrößert dieses Problem jedoch nicht, sondern löst es lediglich nicht. Sie fügen also keine neuen Fehlerquellen hinzu, sondern repräsentieren nur die vorhandenen Fehler im Code anstelle der Waffeldokumentation .]


4
Ein falscher Test lässt gebrochenen Code in die Wildnis. Hier wird der Fehler eingeführt. Es vermittelt ein falsches Gefühl des Vertrauens.
FlySwat

9
Das stimmt, aber wenn Sie keinen Test haben, können Sie auch fehlerhaften Code herauslassen. Der Fehler ist zu denken, dass wenn Code den Unit-Test besteht, er korrekt sein muss - ich wurde ziemlich früh in meiner Karriere davon geheilt. Ein defekter Komponententest lässt kaputten Code also nicht in die Wildnis, sondern nur in Integrationstests.
Steve Jessop

7
Auch ein falscher Test kann fehlerhaften Code abfangen, solange er andere Fehler als die Implementierung enthält. Das ist mein Punkt, dass Tests nicht unbedingt korrekt sein müssen, sondern Ihre Aufmerksamkeit auf Problembereiche lenken sollen.
Steve Jessop

2
sehr interessante Antwort.
Peter

22

Alles, was ich sehe, sind mehr mögliche Fehlerquellen ohne wirklich vorteilhafte Rendite. Wenn der Rabattpreis falsch ist, wird das Testteam immer noch das Problem finden. Wie haben Unit-Tests Arbeit gespart?

Unit-Tests sollen nicht wirklich Arbeit sparen, sondern Ihnen helfen, Fehler zu finden und zu verhindern. Es ist mehr Arbeit, aber es ist die richtige Art von Arbeit. Es geht darum, über Ihren Code mit der niedrigsten Granularität nachzudenken und Testfälle zu schreiben, die beweisen, dass er unter den erwarteten Bedingungen für einen bestimmten Satz von Eingaben funktioniert . Es isoliert Variablen, sodass Sie Zeit sparen können, indem Sie an der richtigen Stelle suchen, wenn ein Fehler auftritt. Es sparend , dass Reihe von Tests , damit Sie sie immer wieder verwenden können , wenn Sie auf der Straße eine Änderung vornehmen müssen.

Ich persönlich denke, dass die meisten Methoden nicht viele Schritte von der Entwicklung von Frachtkult-Software entfernt sind , einschließlich TDD, aber Sie müssen sich nicht an strenge TDD halten, um die Vorteile von Unit-Tests zu nutzen. Behalten Sie die guten Teile und werfen Sie die Teile weg, die wenig Nutzen bringen.

Die Antwort auf Ihre Titelfrage " Wie testest du einen Unit-Test? " Lautet schließlich, dass du das nicht müssen solltest. Jeder Unit-Test sollte hirntot einfach sein. Rufen Sie eine Methode mit einer bestimmten Eingabe auf und vergleichen Sie sie mit der erwarteten Ausgabe. Wenn sich die Spezifikation für eine Methode ändert, können Sie davon ausgehen, dass sich auch einige Komponententests für diese Methode ändern müssen. Dies ist einer der Gründe, warum Sie Unit-Tests mit einer so geringen Granularität durchführen, sodass sich nur einige der Unit-Tests ändern müssen. Wenn Sie feststellen, dass sich Tests für viele verschiedene Methoden für eine Änderung einer Anforderung ändern, testen Sie möglicherweise nicht mit einer ausreichend feinen Granularität.


"Rufen Sie eine Methode mit einer bestimmten Eingabe auf und vergleichen Sie sie mit der erwarteten Ausgabe." aber was ist, wenn die Ausgabe ein komplexer Typ ist ... wie ein XML-Dokument. Sie können nicht einfach "==", Sie müssen einen bestimmten Code-Vergleich schreiben, und dann könnte Ihre Vergleichsmethode möglicherweise fehlerhaft sein?
Andy

@andy: Sie müssen Ihre Vergleichsmethode separat testen. Sobald Sie es gründlich getestet haben, können Sie sich darauf verlassen, dass es in anderen Tests funktioniert.
Bill the Lizard

cool, danke Bill. Ich habe angefangen, an einem neuen Ort zu arbeiten, und es ist mein erstes Mal mit Unit Testing. Ich denke, im Prinzip funktioniert es, und wir verwenden Cruise Control, wo es wirklich nützlich ist, aber große Testreihen scheinen das gleiche Schicksal zu erleiden wie Legacy-Code ... Ich bin mir einfach nicht sicher ...
andy

11

Unit-Tests sind da, damit Ihre Einheiten (Methoden) das tun, was Sie erwarten. Wenn Sie den Test zuerst schreiben, müssen Sie überlegen, was Sie erwarten, bevor Sie den Code schreiben. Vorher denken ist immer eine gute Idee.

Unit-Tests sollten die Geschäftsregeln widerspiegeln. Zugegeben, es kann Fehler im Code geben, aber wenn Sie den Test zuerst schreiben, können Sie ihn aus der Perspektive der Geschäftsregel schreiben, bevor Code geschrieben wurde. Ich denke, dass das anschließende Schreiben des Tests eher zu dem von Ihnen beschriebenen Fehler führt, da Sie wissen, wie der Code ihn implementiert, und versucht sind, nur sicherzustellen, dass die Implementierung korrekt ist - nicht, dass die Absicht korrekt ist.

Außerdem sind Komponententests nur eine Form - und die niedrigste - von Tests, die Sie schreiben sollten. Integrationstests und Abnahmetests sollten ebenfalls geschrieben werden, letztere möglichst vom Kunden, um sicherzustellen, dass das System wie erwartet funktioniert. Wenn Sie während dieses Tests Fehler finden, gehen Sie zurück und schreiben Sie Unit-Tests (die fehlschlagen), um die Änderung der Funktionalität zu testen, damit sie ordnungsgemäß funktioniert. Ändern Sie dann Ihren Code, damit der Test erfolgreich ist. Jetzt haben Sie Regressionstests, die Ihre Fehlerkorrekturen erfassen.

[BEARBEITEN]

Eine andere Sache, die ich bei TDD gefunden habe. Standardmäßig wird ein gutes Design fast erzwungen. Dies liegt daran, dass hochgekoppelte Designs für Isolationstests nahezu unmöglich sind. Mit TDD dauert es nicht lange, um herauszufinden, dass die Verwendung von Schnittstellen, die Umkehrung der Steuerung und die Abhängigkeitsinjektion - alles Muster, die Ihr Design verbessern und die Kopplung verringern - für testbaren Code wirklich wichtig sind.


Vielleicht liegt hier mein Problem. Ich kann den Algorithmus für eine Geschäftsregel einfacher darstellen als das Ergebnis. Daher habe ich kein Problem damit, den Code selbst zu implementieren, sehe die Verspottung der Regel jedoch als redundant an. Vielleicht denke ich nur so.
FlySwat

Genau das machen Sie in einem Unit-Test. Teilen Sie diesen Algorithmus in Teile auf und überprüfen Sie jedes Teil. Normalerweise schreibt sich mein Code selbst, weil ich die Erwartung bereits in meinem Komponententest geschrieben habe.
Tvanfosson

Mock ist ein überladener Begriff im Testbereich. Sie verspotten nicht, was Sie testen möchten, Sie verspotten, was Sie nicht testen möchten ... Wenn Sie den Test für Ihre Geschäftsregel schreiben, erstellen Sie Code, der ihn aufruft - er verspottet ihn überhaupt nicht .
cwash

@cwash - Ich bin nicht sicher, wie Ihr Kommentar auf meine Antwort zutrifft. Ich habe Spott nicht erwähnt ... und ich stimme Ihrer Beobachtung zu.
Tvanfosson

@tvanfosson - Mein letzter Kommentar war eine Antwort auf @FlySwat "... die Regel als überflüssig verspotten." Entschuldigung, ich habe vergessen anzugeben.
cwash

10

Wie testet man einen Test ? Mutationstests sind eine wertvolle Technik, die ich persönlich überraschend gut eingesetzt habe. Lesen Sie den verlinkten Artikel für weitere Details und Links zu noch mehr akademischen Referenzen, aber im Allgemeinen "testet er Ihre Tests", indem Sie Ihren Quellcode ändern (indem Sie beispielsweise "x + = 1" in "x - = 1" ändern) und dann Führen Sie Ihre Tests erneut aus und stellen Sie sicher, dass mindestens ein Test fehlschlägt. Alle Mutationen, die keine Testfehler verursachen, werden zur späteren Untersuchung markiert.

Sie wären überrascht, wie Sie mit einer Reihe von Tests, die umfassend aussehen, eine 100% ige Abdeckung von Leitungen und Zweigen erzielen können, und dennoch können Sie eine Zeile in Ihrer Quelle grundlegend ändern oder sogar auskommentieren, ohne dass sich einer der Tests beschwert. Oft kommt es darauf an, nicht mit den richtigen Eingaben zu testen, um alle Grenzfälle abzudecken, manchmal ist es subtiler, aber in allen Fällen war ich beeindruckt, wie viel daraus herausgekommen ist.


1
+1 interessantes Konzept, von dem ich noch nichts gehört hatte
Wim Coenen

9

Bei der Anwendung von Test-Driven Development (TDD) beginnt man mit einem fehlgeschlagenen Test. Dieser Schritt, der möglicherweise unnötig erscheint, dient dazu, zu überprüfen, ob der Komponententest etwas testet. Wenn der Test niemals fehlschlägt, bringt er keinen Wert und führt zu falschem Vertrauen, da Sie sich auf ein positives Ergebnis verlassen, das nichts beweist.

Wenn Sie diesen Prozess genau befolgen, werden alle "Einheiten" durch das Sicherheitsnetz geschützt, das die Einheitentests herstellen, selbst die banalsten.

Assert.IsEqual(p.DiscountPrice,90);

Es gibt keinen Grund, warum sich der Test in diese Richtung entwickelt - oder ich vermisse etwas in Ihrer Argumentation. Wenn der Preis 100 und der Rabatt 20 beträgt, beträgt der Rabattpreis 80. Dies ist wie eine Invariante.

Stellen Sie sich nun vor, Ihre Software muss eine andere Art von Rabatt unterstützen, basierend auf dem Prozentsatz, möglicherweise abhängig vom gekauften Volumen, und Ihre Product :: DiscountPrice () -Methode wird möglicherweise komplizierter. Und es ist möglich, dass die Einführung dieser Änderungen gegen die einfache Rabattregel verstößt, die wir ursprünglich hatten. Dann sehen Sie den Wert dieses Tests, der die Regression sofort erkennt.


Rot - Grün - Refaktor - Dies soll an die Essenz des TDD-Prozesses erinnern.

Rot bezieht sich auf den roten Balken von JUnit, wenn ein Test fehlschlägt.

Grün ist die Farbe des JUnit-Fortschrittsbalkens, wenn alle Tests bestanden wurden.

Refactor unter grünen Bedingungen: Entfernen Sie alle Duplikationen und verbessern Sie die Lesbarkeit.


Um nun auf die "3-4 Ebenen über dem Code" einzugehen, gilt dies für einen traditionellen (wasserfallähnlichen) Prozess, nicht wenn der Entwicklungsprozess agil ist. Und agil ist die Welt, aus der TDD kommt. TDD ist der Eckpfeiler der eXtreme-Programmierung .

Bei Agile geht es eher um direkte Kommunikation als um über die Mauer geworfene Anforderungsdokumente.


8

Während ich alle für Unit-Tests bin, frage ich mich manchmal, ob diese Form der ersten Testentwicklung wirklich von Vorteil ist ...

Kleine, triviale Tests wie dieser können der "Kanarienvogel im Kohlenbergwerk" für Ihre Codebasis sein und auf Gefahren aufmerksam machen, bevor es zu spät ist. Die trivialen Tests sind nützlich, um auf dem Laufenden zu bleiben, da sie Ihnen helfen, die richtigen Interaktionen zu erzielen.

Denken Sie beispielsweise an einen einfachen Test, mit dem untersucht wird, wie eine API verwendet wird, mit der Sie nicht vertraut sind. Wenn dieser Test für das, was Sie in dem Code tun, der die API "echt" verwendet, relevant ist, ist es nützlich, diesen Test beizubehalten. Wenn die API eine neue Version veröffentlicht und Sie ein Upgrade durchführen müssen. Sie haben jetzt Ihre Annahmen darüber, wie sich die API voraussichtlich in einem ausführbaren Format verhält, mit dem Sie Regressionen abfangen können.

... [I] In einem realen Prozess befinden sich 3-4 Ebenen über Ihrem Code (Geschäftsanforderung, Anforderungsdokument, Architekturdokument), in denen die tatsächlich definierte Geschäftsregel (Rabattpreis ist Preis - Rabatt) möglicherweise falsch definiert ist. In diesem Fall bedeutet Ihnen Ihr Komponententest nichts.

Wenn Sie jahrelang programmiert haben, ohne Tests zu schreiben, ist Ihnen möglicherweise nicht sofort klar, dass es einen Wert gibt. Wenn Sie jedoch der Meinung sind, dass die beste Art zu arbeiten "früh, häufig veröffentlichen" oder "agil" ist, da Sie die Möglichkeit haben möchten, schnell / kontinuierlich bereitzustellen, dann bedeutet Ihr Test definitiv etwas. Die einzige Möglichkeit, dies zu tun, besteht darin, jede Änderung, die Sie am Code vornehmen, mit einem Test zu legitimieren. Egal wie klein der Test ist, sobald Sie eine grüne Testsuite haben, können Sie sie theoretisch bereitstellen. Siehe auch "kontinuierliche Produktion" und "Perpetual Beta".

Sie müssen auch nicht "zuerst testen", um dieser Einstellung zu entsprechen, aber das ist im Allgemeinen der effizienteste Weg, um dorthin zu gelangen. Wenn Sie TDD durchführen, sperren Sie sich in einen kleinen Rot-Grün-Refaktor-Zyklus von zwei bis drei Minuten ein. Zu keinem Zeitpunkt können Sie anhalten und gehen und haben ein komplettes Durcheinander in Ihren Händen, dessen Debuggen und Zusammensetzen eine Stunde dauern wird.

Darüber hinaus ist Ihr Unit-Test ein weiterer Fehlerpunkt ...

Ein erfolgreicher Test zeigt einen Fehler im System. Ein fehlgeschlagener Test macht Sie auf einen Fehler in der Logik des Tests oder in der Logik Ihres Systems aufmerksam. Das Ziel Ihrer Tests ist es, Ihren Code zu brechen oder zu beweisen, dass ein Szenario funktioniert.

Wenn Sie Tests nach dem Code schreiben , laufen Sie Gefahr, einen Test zu schreiben, der "schlecht" ist, denn um zu sehen, dass Ihr Test wirklich funktioniert, müssen Sie sehen, dass er sowohl fehlerhaft als auch funktioniert. Wenn Sie Tests nach dem Code schreiben, bedeutet dies, dass Sie "die Falle stellen" und einen Fehler in den Code einfügen müssen, damit der Test fehlschlägt. Die meisten Entwickler sind sich darüber nicht nur unwohl, sondern würden auch argumentieren, dass dies Zeitverschwendung ist.

Was gewinnen wir hier?

Es ist definitiv ein Vorteil, Dinge auf diese Weise zu tun. Michael Feathers definiert "Legacy-Code" als "ungetesteten Code". Wenn Sie diesen Ansatz wählen, legitimieren Sie jede Änderung, die Sie an Ihrer Codebasis vornehmen. Es ist strenger als keine Tests zu verwenden, aber wenn es darum geht, eine große Codebasis aufrechtzuerhalten, zahlt es sich aus.

Apropos Federn, es gibt zwei großartige Ressourcen, die Sie in diesem Zusammenhang prüfen sollten:

Beide erklären, wie diese Art von Praktiken und Disziplinen in Projekte umgesetzt werden können, die nicht "Greenfield" sind. Sie bieten Techniken zum Schreiben von Tests für eng gekoppelte Komponenten, fest verdrahtete Abhängigkeiten und Dinge, über die Sie nicht unbedingt die Kontrolle haben. Es geht darum, "Nähte" zu finden und diese zu testen.

[I] Wenn der Rabattpreis falsch ist, wird das Testteam immer noch das Problem finden. Wie haben Unit-Tests Arbeit gespart?

Gewohnheiten wie diese sind wie eine Investition. Die Rückgabe erfolgt nicht sofort. Sie bauen sich im Laufe der Zeit auf. Die Alternative zum Nicht-Testen besteht im Wesentlichen darin, die Schuld zu übernehmen, keine Regressionen abfangen zu können, Code ohne Angst vor Integrationsfehlern einzuführen oder Entwurfsentscheidungen zu treffen. Das Schöne ist, dass Sie jede Änderung in Ihrer Codebasis legitimieren.

Was vermisse ich hier? Bitte bringen Sie mir bei, TDD zu lieben, da es mir bisher schwer fällt, es als nützlich zu akzeptieren. Ich will auch, weil ich progressiv bleiben will, aber es macht für mich einfach keinen Sinn.

Ich betrachte es als berufliche Verantwortung. Es ist ein Ideal, um danach zu streben. Aber es ist sehr schwer zu folgen und langweilig. Wenn Sie sich darum kümmern und der Meinung sind, dass Sie keinen Code produzieren sollten, der nicht getestet wurde, können Sie die Willenskraft finden, um gute Testgewohnheiten zu erlernen. Eine Sache, die ich jetzt viel mache (wie andere auch), ist, mir eine Stunde Zeit zu geben, um Code ohne Tests zu schreiben und dann die Disziplin zu haben, ihn wegzuwerfen. Das mag verschwenderisch erscheinen, ist es aber nicht wirklich. Es ist nicht so, dass Übung physische Materialien eines Unternehmens kostet. Es hat mir geholfen, das Problem zu verstehen und Code so zu schreiben, dass er sowohl von höherer Qualität als auch testbar ist.

Mein Rat wäre letztendlich, wenn Sie wirklich nicht den Wunsch haben, gut darin zu sein, dann tun Sie es überhaupt nicht. Schlechte Tests, die nicht gewartet werden, nicht gut funktionieren usw. können schlimmer sein als keine Tests. Es ist schwer, alleine zu lernen, und Sie werden es wahrscheinlich nicht lieben, aber es wird so gut wie unmöglich sein, es zu lernen, wenn Sie nicht den Wunsch haben, es zu tun, oder nicht genug Wert darin sehen können garantieren die Zeitinvestition.

Ein paar Leute erwähnen immer wieder, dass Tests helfen, die Spezifikation durchzusetzen. Ich habe die Erfahrung gemacht, dass die Spezifikation auch meistens falsch war ...

Auf der Tastatur eines Entwicklers trifft der Gummi auf die Straße. Wenn die Spezifikation falsch ist und Sie die Flagge nicht darauf hissen, werden Sie höchstwahrscheinlich dafür verantwortlich gemacht. Oder zumindest Ihr Code wird. Die Disziplin und Genauigkeit, die mit dem Testen verbunden ist, ist schwer einzuhalten. Es ist überhaupt nicht einfach. Es braucht Übung, viel Lernen und viele Fehler. Aber irgendwann zahlt es sich aus. Bei einem schnelllebigen, sich schnell ändernden Projekt ist dies die einzige Möglichkeit, nachts zu schlafen, unabhängig davon, ob es Sie verlangsamt.

Eine andere Sache, über die man hier nachdenken sollte, ist, dass Techniken, die im Wesentlichen mit dem Testen identisch sind, in der Vergangenheit nachweislich funktionieren: "Reinraum" und "Design by Contract" erzeugen beide tendenziell die gleichen Arten von "Meta" -Code-Konstrukten, die Tests tun und erzwingen diese an verschiedenen Punkten. Bei keiner dieser Techniken handelt es sich um Silberkugeln, und die Genauigkeit kostet Sie letztendlich den Umfang der Funktionen, die Sie in Bezug auf die Markteinführungszeit bereitstellen können. Aber darum geht es nicht. Es geht darum, das zu erhalten, was Sie liefern. Und das ist für die meisten Projekte sehr wichtig.


7

Unit-Tests funktionieren sehr ähnlich wie die doppelte Buchführung. Sie geben dasselbe (Geschäftsregel) auf zwei ganz unterschiedliche Arten an (als programmierte Regeln in Ihrem Produktionscode und als einfache, repräsentative Beispiele in Ihren Tests). Es ist sehr unwahrscheinlich, dass Sie in beiden Fällen den gleichen Fehler machen. Wenn also beide übereinstimmen, ist es eher unwahrscheinlich, dass Sie etwas falsch gemacht haben.

Wie lohnt sich das Testen? Nach meiner Erfahrung auf mindestens vier Arten, zumindest bei testgetriebener Entwicklung:

  • Es hilft Ihnen dabei, ein gut entkoppeltes Design zu entwickeln. Sie können nur Unit-Test-Code verwenden, der gut entkoppelt ist.
  • Es hilft Ihnen festzustellen, wann Sie fertig sind. Wenn Sie das erforderliche Verhalten in Tests angeben müssen, können Sie keine Funktionen erstellen, die Sie nicht benötigen, und feststellen, wann die Funktionen vollständig sind.
  • Es gibt Ihnen ein Sicherheitsnetz für Refactorings, wodurch der Code für Änderungen viel zugänglicher wird. und
  • Es spart Ihnen viel Debugging-Zeit, was schrecklich teuer ist (ich habe Schätzungen gehört, dass Entwickler traditionell bis zu 80% ihrer Zeit mit dem Debuggen verbringen).

5

Die meisten Unit-Tests, Testannahmen. In diesem Fall sollte der Rabattpreis der Preis abzüglich des Rabattes sein. Wenn Ihre Annahmen falsch sind, ist Ihr Code wahrscheinlich auch falsch. Und wenn Sie einen dummen Fehler machen, schlägt der Test fehl und Sie korrigieren ihn.

Wenn sich die Regeln ändern, schlägt der Test fehl und das ist gut so. In diesem Fall müssen Sie also auch den Test ändern.

Wenn ein Test sofort fehlschlägt (und Sie nicht das erste Testdesign verwenden), ist in der Regel entweder der Test oder der Code falsch (oder beides, wenn Sie einen schlechten Tag haben). Sie verwenden den gesunden Menschenverstand (und möglicherweise die Spezifikationen), um den fehlerhaften Code zu korrigieren und den Test erneut auszuführen.

Wie Jason sagte, ist Testen Sicherheit. Und ja, manchmal führen sie aufgrund fehlerhafter Tests zusätzliche Arbeit ein. Aber meistens sparen sie viel Zeit. (Und Sie haben die perfekte Gelegenheit, den Mann zu bestrafen, der den Test bricht (wir sprechen von Gummihuhn)).


4

Testen Sie alles, was Sie können. Selbst triviale Fehler wie das Vergessen, Meter in Fuß umzurechnen, können sehr teure Nebenwirkungen haben. Schreiben Sie einen Test, schreiben Sie den Code, damit er überprüft wird, lassen Sie ihn bestehen, fahren Sie fort. Wer weiß, dass irgendwann in der Zukunft jemand den Rabattcode ändern kann. Ein Test kann das Problem erkennen.


Das spricht keinen meiner Gedanken an. Ich verstehe das grundlegende Mantra von TDD ... Ich sehe den Nutzen nicht.
FlySwat

4

Ich sehe Unit-Tests und Produktionscode als eine symbiotische Beziehung. Einfach ausgedrückt: Einer testet den anderen. Und beide testen den Entwickler.


3

Denken Sie daran, dass die Kosten für die Behebung von Fehlern (exponentiell) steigen, wenn die Fehler den Entwicklungszyklus durchlaufen. Ja, das Testteam erkennt den Fehler möglicherweise, aber es wird (normalerweise) mehr Arbeit erfordern, um den Fehler von diesem Punkt an zu isolieren und zu beheben, als wenn ein Komponententest fehlgeschlagen wäre, und es wird einfacher sein, andere Fehler einzuführen, während Sie ihn beheben, wenn Sie dies tun Es müssen keine Komponententests ausgeführt werden.

Das ist normalerweise mit etwas mehr als einem trivialen Beispiel leichter zu erkennen ... und mit trivialen Beispielen, wenn Sie den Komponententest irgendwie durcheinander bringen, wird die Person, die ihn überprüft, den Fehler im Test oder den Fehler im Code erkennen, oder beide. (Sie werden überprüft, richtig?) Wie tvanfosson betont, sind Unit-Tests nur ein Teil eines SQA-Plans.

Unit-Tests sind gewissermaßen Versicherungen. Sie sind keine Garantie dafür, dass Sie jeden Fehler erkennen, und es scheint manchmal, als würden Sie eine Menge Ressourcen für sie ausgeben, aber wenn sie Fehler erkennen, die Sie beheben können, werden Sie viel weniger ausgeben als wenn Sie überhaupt keine Tests gehabt hätten und alle Mängel stromabwärts beheben müssten.


3

Ich verstehe Ihren Standpunkt, aber er ist deutlich übertrieben.

Ihr Argument lautet im Grunde: Tests führen zu Fehlern. Daher sind Tests schlecht / Zeitverschwendung.

Während dies in einigen Fällen zutreffen mag, ist es kaum die Mehrheit.

TDD geht davon aus: Mehr Tests = weniger Fehler.

Tests erkennen Fehlerquellen eher als führen sie ein.


1

Hier kann noch mehr Automatisierung helfen! Ja, das Schreiben von Komponententests kann eine Menge Arbeit bedeuten. Verwenden Sie daher einige Tools, um Ihnen zu helfen. Schauen Sie sich etwas wie Pex von Microsoft an, wenn Sie .Net verwenden. Es erstellt automatisch eine Reihe von Komponententests für Sie, indem Sie Ihren Code untersuchen. Es werden Tests erstellt, die eine gute Abdeckung bieten und versuchen, alle Pfade durch Ihren Code abzudecken.

Wenn Sie sich nur Ihren Code ansehen, können Sie natürlich nicht wissen, was Sie tatsächlich versucht haben. Sie wissen also nicht, ob er korrekt ist oder nicht. Es werden jedoch interessante Testfälle für Sie generiert, und Sie können sie dann untersuchen und feststellen, ob sie sich wie erwartet verhalten.

Wenn Sie dann weiter gehen und parametrisierte Komponententests schreiben (Sie können sich diese tatsächlich als Verträge vorstellen), werden daraus spezifische Testfälle generiert, und diesmal kann es feststellen, ob etwas nicht stimmt, da Ihre Aussagen in Ihren Tests fehlschlagen.


1

Ich habe ein wenig über eine gute Möglichkeit nachgedacht, auf diese Frage zu antworten, und möchte eine Parallele zur wissenschaftlichen Methode ziehen. IMO, Sie könnten diese Frage umformulieren: "Wie experimentieren Sie ein Experiment?"

Experimente bestätigen empirische Annahmen (Hypothesen) über das physikalische Universum. Unit-Tests testen Annahmen über den Status oder das Verhalten des von ihnen aufgerufenen Codes. Wir können über die Gültigkeit eines Experiments sprechen, aber das liegt daran, dass wir durch zahlreiche andere Experimente wissen, dass etwas nicht passt. Es hat nicht sowohl konvergente Gültigkeit als auch empirische Beweise . Wir entwerfen nicht ein neues Experiment zu testen oder die Gültigkeit zu überprüfen ein Experiment , aber wir können ein Design völlig neues Experiment .

So wie bei Experimenten beschreiben wir die Gültigkeit eines Komponententests nicht danach, ob er einen Komponententest selbst besteht oder nicht. Zusammen mit anderen Komponententests werden die Annahmen beschrieben, die wir über das zu testende System treffen. Ebenso wie bei Experimenten versuchen wir, so viel Komplexität wie möglich aus dem zu entfernen, was wir testen. "So einfach wie möglich, aber nicht einfacher."

Im Gegensatz zu Experimenten haben wir einen Trick im Ärmel, um zu überprüfen, ob unsere Tests gültig sind und nicht nur eine konvergente Gültigkeit. Wir können geschickt einen Fehler einführen, von dem wir wissen, dass er vom Test erfasst werden sollte, und prüfen, ob der Test tatsächlich fehlschlägt. (Wenn wir das nur in der realen Welt tun könnten, wären wir viel weniger von dieser konvergenten Gültigkeitssache abhängig!) Eine effizientere Möglichkeit, dies zu tun, besteht darin, zu beobachten, wie Ihr Test fehlschlägt, bevor Sie ihn implementieren (der rote Schritt in Rot, Grün, Refactor) ).


1

Sie müssen das richtige Paradigma verwenden, wenn Sie Tests schreiben.

  1. Schreiben Sie zunächst Ihre Tests.
  2. Stellen Sie sicher, dass sie nicht mit beginnen.
  3. Bring sie zum Bestehen.
  4. Codeüberprüfung, bevor Sie Ihren Code einchecken (stellen Sie sicher, dass die Tests überprüft werden.)

Sie können nicht immer sicher sein, aber sie verbessern die Gesamttests.


0

Selbst wenn Sie Ihren Code nicht testen, wird er sicherlich von Ihren Benutzern in der Produktion getestet. Benutzer sind sehr kreativ, wenn es darum geht, Ihre Software zum Absturz zu bringen und selbst unkritische Fehler zu finden.

Das Beheben von Fehlern in der Produktion ist viel teurer als das Beheben von Problemen in der Entwicklungsphase. Als Nebeneffekt verlieren Sie aufgrund eines Exodus von Kunden Einkommen. Sie können auf 11 verlorene oder nicht gewonnene Kunden für 1 verärgerten Kunden zählen.

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.