Bestimmen, was ein nützlicher Komponententest ist


46

Ich habe die Dokumente von phpunit durchgesehen und bin auf folgendes Zitat gestoßen:

Sie können immer mehr Tests schreiben. Sie werden jedoch schnell feststellen, dass nur ein Bruchteil der Tests, die Sie sich vorstellen können, tatsächlich nützlich sind. Sie möchten Tests schreiben, die fehlschlagen, obwohl Sie glauben, dass sie funktionieren sollten, oder Tests, die erfolgreich sind, obwohl Sie glauben, dass sie fehlschlagen sollten. Eine andere Sichtweise ist das Kosten-Nutzen-Verhältnis. Sie möchten Tests schreiben, die Sie mit Informationen zurückzahlen. - Reiches Gamma

Ich habe mich gefragt. Wie bestimmen Sie, was einen Komponententest nützlicher macht als einen anderen, abgesehen davon, was in diesem Zitat über Kosten / Nutzen angegeben ist? Wie entscheiden Sie, für welchen Teil Ihres Codes Sie Komponententests erstellen? Ich frage das, weil ein anderes dieser Zitate auch sagte:

Wenn es also nicht um Testen geht, worum geht es dann? Es geht darum, herauszufinden, was Sie versuchen, bevor Sie halb gespannt davonlaufen, um es zu versuchen. Sie schreiben eine Spezifikation, die einen kleinen Aspekt des Verhaltens in einer präzisen, eindeutigen und ausführbaren Form festhält. So einfach ist das. Heißt das, Sie schreiben Tests? Nein. Das bedeutet, Sie schreiben Spezifikationen, was Ihr Code tun muss. Dies bedeutet, dass Sie das Verhalten Ihres Codes im Voraus festlegen. Aber nicht weit vor der Zeit. In der Tat ist es am besten, kurz bevor Sie den Code schreiben, da Sie dann so viele Informationen zur Hand haben, wie Sie bis dahin wollen. Wie bei gut gemachtem TDD arbeiten Sie in winzigen Schritten ... indem Sie jeweils einen kleinen Aspekt des Verhaltens festlegen und dann implementieren. Wenn Sie feststellen, dass es nur darum geht, Verhalten zu spezifizieren und keine Tests zu schreiben, Ihr Standpunkt verschiebt sich. Plötzlich ist die Idee, für jede Ihrer Produktionsklassen eine Testklasse zu haben, lächerlich einschränkend. Und der Gedanke, jede Ihrer Methoden mit einer eigenen Testmethode (in einer 1: 1-Beziehung) zu testen, wird lächerlich sein. - Dave Astels

Der wichtige Teil davon ist

* Und der Gedanke, jede Ihrer Methoden mit einer eigenen Testmethode (in einer 1: 1-Beziehung) zu testen, ist lächerlich. *

Wenn es also lächerlich ist, für jede Methode einen Test zu erstellen, wie / wann haben Sie dann ausgewählt, wofür Sie Tests schreiben?


5
Es ist eine gute Frage, aber ich denke, es ist eine programmiersprachenunabhängige Frage - warum haben Sie sie mit PHP getaggt?

Hier sind einige wirklich gute Artikel, die mir eine gute Anleitung dazu gaben, welche Komponententests ich schreiben sollte und für welche Bereiche es wichtig ist, automatisierte Tests bereitzustellen. - blog.stevensanderson.com/2009/11/04/… - blog.stevensanderson.com/2009/08/24/… - ayende.com/blog/4218/scenario-driven-tests
Kane

Antworten:


26

Wie viele Tests pro Methode?

Nun, das theoretische und höchst unpraktische Maximum ist die N-Pfad-Komplexität (vorausgesetzt, die Tests decken alle verschiedene Wege durch den Code ab;)). Das Minimum ist EINS !. Gemäß der öffentlichen Methode testet er keine Implementierungsdetails, sondern nur das externe Verhalten einer Klasse (Rückgabewerte und Aufrufen anderer Objekte).

Sie zitieren:

* Und der Gedanke, jede Ihrer Methoden mit einer eigenen Testmethode (in einer 1: 1-Beziehung) zu testen, ist lächerlich. *

und dann fragen:

Wenn es also lächerlich ist, für jede Methode einen Test zu erstellen, wie / wann haben Sie dann ausgewählt, wofür Sie Tests schreiben?

Aber ich denke, Sie haben den Autor hier falsch verstanden:

Die Idee, one test methodPer zu haben, one method in the class to testnennt der Autor "lächerlich".

(Zumindest für mich) Es geht nicht um "weniger", es geht um "mehr"

Lassen Sie mich so umformulieren, als hätte ich ihn verstanden:

Und der Gedanke, jede Ihrer Methoden mit NUR EINER METHODE (einer eigenen Testmethode in einer 1: 1-Beziehung) zu testen, wird lächerlich sein.

So zitieren Sie Ihr Angebot erneut:

Wenn Sie feststellen, dass es nur darum geht, Verhalten zu spezifizieren und keine Tests zu schreiben, ändert sich Ihre Sichtweise.


Wenn Sie TDD üben, denken Sie nicht :

Ich habe eine Methode calculateX($a, $b);und es braucht einen Test testCalculcateX, der ALLES über die Methode testet.

Was TDD Ihnen sagt, ist darüber nachzudenken, was Ihr Code tun sollte :

Ich muss den größeren von zwei Werten berechnen ( erster Testfall! ), Aber wenn $ a kleiner als Null ist, sollte es einen Fehler erzeugen ( zweiter Testfall! ) Und wenn $ b kleiner als Null ist, sollte es ... ( dritter Testfall! ) und so weiter.


Sie möchten Verhalten testen, nicht nur einzelne Methoden ohne Kontext.

Auf diese Weise erhalten Sie eine Testsuite, die Dokumentation für Ihren Code ist und WIRKLICH erklärt, was von ihm erwartet wird, vielleicht sogar warum :)


Wie entscheiden Sie, für welchen Teil Ihres Codes Sie Komponententests erstellen?

Nun, alles, was im Endlager oder irgendwo in der Nähe der Produktion landet, muss getestet werden. Ich glaube nicht, dass der Autor Ihrer Zitate damit nicht einverstanden wäre, wie ich oben dargelegt habe.

Wenn Sie keinen Test dafür haben, wird es viel schwieriger (teurer), den Code zu ändern, besonders wenn Sie die Änderung nicht vornehmen.

TDD ist eine Möglichkeit, um sicherzustellen, dass Sie Tests für ALLES haben, aber solange Sie die Tests schreiben, ist es in Ordnung. Normalerweise hilft es, sie am selben Tag zu schreiben, da Sie es später nicht tun werden, oder? :)



Antwort auf Kommentare:

Eine anständige Anzahl von Methoden kann in einem bestimmten Kontext nicht getestet werden, da sie entweder von anderen Methoden abhängen oder von diesen abhängig sind

Nun, diese Methoden können drei Dinge aufrufen:

Öffentliche Methoden anderer Klassen

Wir können andere Klassen verspotten, also haben wir dort den Status definiert. Wir haben die Kontrolle über den Kontext, so dass dies kein Problem ist.

* Geschützte oder private Methoden auf dem gleichen *

Alles, was nicht Teil der öffentlichen API einer Klasse ist, wird normalerweise nicht direkt getestet.

Sie möchten das Verhalten und nicht die Implementierung testen, und wenn eine Klasse alles tut, funktioniert dies in einer großen öffentlichen Methode oder in vielen kleineren geschützten Methoden, die aufgerufen werden, in der Implementierung . Sie möchten in der Lage sein, diese geschützten Methoden ZU ÄNDERN, OHNE Ihre Tests zu berühren. Denn Ihre Tests brechen ab, wenn Ihr Code das Verhalten ändert! Dafür sind deine Tests da, um dir zu sagen, wann du etwas kaputt machst :)

Öffentliche Methoden für dieselbe Klasse

Das kommt nicht sehr oft vor, oder? Und wenn es wie im folgenden Beispiel aussieht, gibt es ein paar Möglichkeiten, damit umzugehen:

$stuff = new Stuff();
$stuff->setBla(12);
$stuff->setFoo(14);
$stuff->execute(); 

Dass die Setter existieren und nicht Teil der Signatur der Execute-Methode sind, ist ein anderes Thema;)

Was wir hier testen können, ist, ob Executes explodieren, wenn wir die falschen Werte einstellen. Das setBlalöst eine Ausnahme aus, wenn Sie eine Zeichenfolge übergeben, die separat getestet werden kann. Wenn Sie jedoch testen möchten, dass diese beiden zulässigen Werte (12 und 14) nicht ZUSAMMEN (aus welchem ​​Grund auch immer) funktionieren, ist dies nur ein Testfall.

Wenn Sie eine "gute" Testsuite wollen, können Sie in PHP vielleicht (!) Eine @covers Stuff::executeAnmerkung hinzufügen , um sicherzustellen, dass Sie nur Code-Coverage für diese Methode generieren, und die anderen Dinge, die gerade eingerichtet werden, müssen separat getestet werden (noch einmal, wenn Du willst das).

Der Punkt ist also: Vielleicht müssen Sie zuerst einen Teil der umgebenden Welt erstellen, aber Sie sollten in der Lage sein, aussagekräftige Testfälle zu schreiben, die normalerweise nur eine oder vielleicht zwei reale Funktionen umfassen (Setter zählen hier nicht). Der Rest kann verspottet oder zuerst getestet und dann als Referenz herangezogen werden (siehe @depends).


* Hinweis: Die Frage wurde von SO migriert und betraf ursprünglich PHP / PHPUnit. Aus diesem Grund stammen der Beispielcode und die Referenzen aus der PHP-Welt. Ich denke, dies gilt auch für andere Sprachen, da sich phpunit nicht wesentlich von anderen xUnit unterscheidet Testen von Frameworks.


Sehr detailliert und informativ ... Sie sagten: "Sie möchten Verhalten testen, nicht nur einzelne Methoden ohne Kontext." Sicherlich kann eine anständige Menge von Methoden in einem bestimmten Kontext nicht getestet werden, da sie entweder von anderen Methoden abhängen oder von diesen abhängig sind wäre daher eine nützliche Testbedingung nur dann kontextbezogen, wenn auch die abhängigen Personen getestet würden? Oder interpretiere ich falsch, was Sie meinen
?

@ robinsonc494 Ich bearbeite in einem Beispiel, das vielleicht etwas besser erklärt, wohin ich mit diesem gehe
edorian

danke für die bearbeitungen und das beispiel, das hilft bestimmt. Ich glaube, meine Verwirrung (wenn Sie es so nennen können) war, dass ich, obwohl ich über das Testen auf "Verhalten" gelesen habe, irgendwie (vielleicht?) An Testfälle gedacht habe, die sich auf die Implementierung konzentrierten.
Zcourts

@ robinsonc494 Stell es dir vielleicht so vor: Wenn du jemanden schlägst, schlägt er zurück, ruf die Polizei oder rennt weg. Dieses Verhalten. Es ist das, was die Person tut. Die Tatsache, dass er seine Muscheln verwendet, die durch geringe elektrische Ladungen aus seinem Gehirn ausgelöst werden, ist eine Umsetzung. Wenn Sie jemandes Reaktion testen möchten, schlagen Sie ihn und sehen, ob er sich so verhält, wie Sie es erwarten. Sie legen ihn nicht in einen Gehirnscanner und prüfen, ob die Impulse an die Muscheln gesendet werden. Einige gehen so ziemlich in den Unterricht;)
Edorian

3
Ich denke, das beste Beispiel, das ich für TDD gesehen habe und das es wirklich geholfen hat, die Testfälle zu schreiben, war das Bowling-Spiel Kata von Onkel Bob Martin. Mitfahrgelegenheit.net/lalitkale/bowling-game-kata-by-robert-c-martin
Amy Anuszewski 07.07.11

2

Testen und Unit-Testen sind nicht dasselbe. Unit Testing ist eine sehr wichtige und interessante Untergruppe von Tests insgesamt. Ich würde behaupten, dass der Schwerpunkt des Unit-Testings uns dazu bringt, über diese Art des Testens in einer Weise nachzudenken, die den obigen Zitaten etwas widerspricht.

Erstens, wenn wir TDD oder sogar DTH befolgen (dh in enger Harmonie entwickeln und testen), verwenden wir die Tests, die wir schreiben, um unser Design richtig zu machen. Wenn wir über Eckfälle nachdenken und entsprechende Tests schreiben, vermeiden wir, dass die Fehler überhaupt erst auftreten. Wir schreiben also Tests, die wir für bestanden halten (OK, gleich zu Beginn des TDD, sie schlagen fehl, aber das ist nur ein Bestellungsartefakt, wenn Der Code ist fertig, wir erwarten, dass sie bestanden werden, und die meisten tun dies, weil Sie über den Code nachgedacht haben.

Zweitens kommen die Unit Tests erst richtig zur Geltung, wenn wir überarbeiten. Wir ändern unsere Implementierung, erwarten jedoch, dass die Antworten gleich bleiben - der Unit-Test schützt uns vor dem Bruch unseres Schnittstellenvertrags. Also noch einmal, wir erwarten, dass die Tests bestehen.

Dies bedeutet, dass wir für unsere öffentliche Schnittstelle, die vermutlich stabil ist, eine eindeutige Rückverfolgbarkeit benötigen, damit wir sehen können, dass jede öffentliche Methode getestet wird.

Um Ihre explizite Frage zu beantworten: Unit Test für öffentliche Schnittstelle haben Wert.

bearbeitet im Antwortkommentar:

Private Methoden testen? Ja, wir sollten, aber wenn wir etwas nicht testen müssen, würde ich hier Kompromisse eingehen. Wenn die öffentlichen Methoden funktionieren, können dann diese Fehler im privaten Bereich so wichtig sein? Pragmatisch gesehen ist die Abwanderung im privaten Bereich eher selten, Sie arbeiten hart daran, Ihre öffentliche Schnittstelle zu pflegen, aber wenn sich die Dinge ändern, auf die Sie angewiesen sind, können sich die privaten Dinge ändern. Irgendwann stellen wir möglicherweise fest, dass die internen Tests sehr aufwändig sind. Ist diese Anstrengung sinnvoll?


Nur um sicherzugehen, dass ich verstehe, was Sie sagen: Konzentrieren Sie sich beim Testen darauf, die öffentliche Schnittstelle zu testen, richtig? Vorausgesetzt, das ist richtig ... gibt es nicht eine größere Möglichkeit, Fehler in privaten Methoden / Schnittstellen zu hinterlassen, für die Sie keine Komponententests durchgeführt haben? Einige knifflige Fehler in der nicht getesteten "privaten" Schnittstelle könnten möglicherweise dazu führen, dass ein Test bestanden wird, wenn dies der Fall ist hätte wirklich scheitern sollen. Irre ich mich?
Zcourts

Anhand der Codeabdeckung können Sie feststellen, wann Code in Ihren privaten Methoden nicht ausgeführt wird, während Sie Ihre öffentlichen Methoden testen. Wenn Ihre öffentlichen Methoden vollständig abgedeckt sind, werden alle nicht abgedeckten privaten Methoden offensichtlich nicht verwendet und können entfernt werden. Wenn nicht, benötigen Sie weitere Tests für Ihre öffentlichen Methoden.
David Harkness

2

Unit-Tests sollten Teil einer umfassenderen Teststrategie sein. Ich folge diesen Grundsätzen bei der Auswahl der zu schreibenden Testarten und wann:

  • Konzentrieren Sie sich auf das Schreiben von End-to-End-Tests. Sie decken mehr Code pro Test als bei Unit-Tests ab und erhalten so mehr Testknall für das Geld. Machen Sie diese zur vollautomatischen Validierung Ihres gesamten Systems.

  • Schreiben Sie Unit-Tests zu Nuggets komplizierter Logik. Komponententests sind in Situationen, in denen End-to-End-Tests für eine angemessene Codeabdeckung schwer zu debuggen oder schwer zu schreiben sind, ihr Gewicht zu verdienen.

  • Warten Sie, bis die API, gegen die Sie testen, stabil ist , um einen der beiden Testtypen zu schreiben. Sie möchten vermeiden, dass sowohl Ihre Implementierung als auch Ihre Tests überarbeitet werden müssen.

Rob Ashton hat einen guten Artikel zu diesem Thema, aus dem ich mich stark gemacht habe, um die oben genannten Prinzipien zu artikulieren.


+1 - Ich stimme nicht unbedingt mit allen Ihren Punkten (oder den Punkten des Artikels) überein, aber ich stimme zu, dass die meisten Komponententests unbrauchbar sind, wenn sie blind durchgeführt werden (TDD-Ansatz). Sie sind jedoch äußerst wertvoll, wenn Sie klug entscheiden, was es wert ist, Zeit mit Unit-Tests zu verbringen. Ich stimme voll und ganz zu, dass Sie beim Schreiben von Tests auf höherer Ebene, insbesondere automatisierten Tests auf Subsystemebene, weitaus mehr Geld verdienen. Das Problem bei durchgängigen automatisierten Tests ist, dass sie für ein System mit beliebiger Größe / Komplexität schwierig, wenn nicht sogar völlig unpraktisch sind.
Dunk

0

Ich tendiere dazu, einen anderen Ansatz zum Testen von Einheiten zu verfolgen, der anscheinend gut funktioniert. Anstatt einen Unit-Test als "Testen eines Verhaltens" zu betrachten, betrachte ich ihn eher als "eine Spezifikation, der mein Code folgen muss". Auf diese Weise können Sie im Grunde deklarieren, dass sich ein Objekt auf eine bestimmte Weise verhalten soll, und vorausgesetzt, dass Sie an anderer Stelle in Ihrem Programm davon ausgehen, dass es relativ fehlerfrei ist.

Wenn Sie eine öffentliche API schreiben, ist dies äußerst wertvoll. Sie werden jedoch auch immer eine gute Dosis von End-to-End-Integrationstests benötigen, da sich eine Annäherung an eine 100% -ige Abdeckung von Komponententests normalerweise nicht lohnt und Dinge übersehen werden, die die meisten nach Komponententestmethoden als "nicht testbar" erachten (Verspotten, usw)

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.