Unit Test ... wie aufwändig darf es sein

[QUOTE=CyborgGamma]Wenn ich dich richtig verstehe hast du eine Klasse die mit einem Parameter aufgerufen wird und diese soll dann (wenn dort ein Wert gesetzt ist) einen Aufruf machen und wenn nicht einen Fehler Werfen.
Nun würde ich für den Test die Methode die Aufgerufen wird Mocken (z.B. durch Frameworks wie http://mockito.org/) und da kann man dann schauen ob der Aufruf mit dem richtigen Parameter gemacht wurde (java - How to use ArgumentCaptor for stubbing? - Stack Overflow).
Somit einmal mit dem Gültigen wert Foo aufrufen und in dem Mock schauen ob er aufgerufen wurde (mit dem passenden SQL Statement) und einen gültige Antwort zurück geben und dieses dann auch nochmal checken ob das auch deine Methode dann passend weiter zurück reicht und einmal ohne Eingabe aufrufen, schauen ob die Exception fliegt und dein Mock auch nicht aufgerufen wurde.[/QUOTE]
du willst du Methode die getestet werden soll mocken ? Das ist doch totaler Unsinn. Man mockt nur Abhängigkeiten, niemals den zu testenden Teil ! und wenn du die Methode mockst die teil des tests ist und dann deren Ergebnis überprüfst, testest du den mock.

Entweder völlig daneben, oder so komisch beschrieben dass ich es nicht verstehe

*** Edit ***

Ups das ist mir unterm Radar abgehauen.

Damit würde ich vorsichtig sein. Man sollte beim Testen immer ein Risk Management haben/machen - es geht die Wahrscheinlichkeit und severity (Schwere?) zu beurteilen und je nach dem ist die Stelle zu testen. Die Häufigkeit der Verwendung spielt keine Rolle.

OK. Danke an alle … habe schon das Gefühl Dinge zu testen zu wollen, die gar nicht Aufgabe der Methode ist. Aber wann setzt man dann die Initializer von @Greta ein? Um welche Tests handelt es sich da? Integrationstests / UI Tests?

@bygones die Methode die man testen will zu mocken ist ja absoluter Schwachsinn, dann kann man den Versuch etwas zu testen auch gleich lassen. Wenn man künstlich viele Ausgeführte test haben will dann kann ich auch schreiben


@Test
public void testTrue(){
   assertTrue(true);
}

aber einen Mehrwert habe ich dadurch nicht.

Du hast häufig Methoden die deine Methode aufruft(Stubs welche man mockt) und Methoden die deine Methode aufrufen (Driver für den Test dein UnitTest)
Nun sollte man nun diese Stubs wie gesagt mocken. Und bei diesem Stub kann ich nun schauen ob ich diesen mit dem passenden wert aufgerufen habe (somit der Output meiner Methode [welche ich nun testen will] den richtigen Output generiert) und auf der anderen Seite in meinem Driver kann ich schauen ob der return wert meiner Methode der richtige ist (wird häufig mit einem assert() in dem Unittest gemacht)

Wer macht das? Der Tester, der Entwickler, oder jemand ganz anderes? Und unter welchen Gesichtspunkten entscheidet man das? Wie schon gesagt, ist es für mich absolut logisch, dass ich eine Lib, die tausendfach verwendet wird auf Herz und Nieren teste. Aber wann schreibe ich keinen Test mehr? Ich höre ja immer nur „alles“ … aber den Sinn dahinter erklären kann mir in der Regel keiner.

Das ist wie schon die ganze Zeit gesagt immer nach Kontext,… unterschiedlich, diese Initializer können von dem Unit test bis hin zum UI-Test/Integrationstest verwendet werden

@CyborgGamma du willst einen Stub mocken ? Warum dann überhaupt einen Stub haben wenn die Funktionalität gemockt werden soll? sicher du meinst mit Stub nicht was anderes ?

Methodenaufrufe aus deiner SUT mockt man nicht per se. Wie gesagt, Stubs, mocks and friends sind dazu da deine Unit zu isolieren, nicht einzelne Methoden

„alles“ testen ist eine Totsuende. Es bringt weder was alles zu testen und kostet zu viel. Daher macht man ja u.a solche Riskanalysen. Wie gesagt, die Qualität einer Software sollte nicht aufgrund ihrer Verwendung definiert werden. Die Qualität sollte immer hoch sein.

Wer Riskmanagement macht hängt von dem Team/Organisation ab. Im normallfall nicht der Entwickler. Product Owner, Projekt Manager, QA Manager etc etc kommen in Frage. Wenn ein Entwickler alle diese Rollen in einem Projekt übernimmt dann natürlich er.

Um meins nochmal zu verdeutlichen
A (Mein UnitTest) ruft B (meine eigentliche Funktion) mit einem Wert auf diese ruft nun C (meinen Mock) auf. Nun gibt C einen wert zurück an B was diesen noch eventuell weiterverarbeitet und an A zurück gibt. Nun kann ich bei A schauen ob der Wert von B der richtige ist und ob C mit dem richtigen wert aufgerufen wurde (mittels wie schon geschrieben java - How to use ArgumentCaptor for stubbing? - Stack Overflow)
Als zweiten Test ruft A (Mein UnitTest) nun B (meine eigentliche Funktion) mit einem ungültigen Wert auf und gibt eine Exception an A zurück. Nun kann ich bei A schauen ob eine Exception gekommen ist und das C nicht aufgerufen wurde.

Das Risiko Analysieren (vorallem für Integrationstest,… sollte der Testmanager machen) bei einem Unit test würde ich wie schon gesagt alles Testen.

Bei uns verschwimmen die Unit-Tests mit Integrationstest. Wenn wir Service-Methoden testen, die mit DB-Objekten arbeiten, nutzen wir die Initializer.

Beispiel:
Der Nachname eines Nutzer in der DB soll geupdatet werden.

Der Initialier legt einen Nutzer in der DB an.

Der Test lädt sich diesen Nutzer und verwendet die entsprechende Service-Methode zum Ändern. Danach wird der Nutzer wieder geladen und der „neue“ Nachname geprüft.

[QUOTE=CyborgGamma]Um meins nochmal zu verdeutlichen
A (Mein UnitTest) ruft B (meine eigentliche Funktion) mit einem Wert auf diese ruft nun C (meinen Mock) auf. Nun gibt C einen wert zurück an B was diesen noch eventuell weiterverarbeitet und an A zurück gibt. Nun kann ich bei A schauen ob der Wert von B der richtige ist und ob C mit dem richtigen wert aufgerufen wurde (mittels wie schon geschrieben java - How to use ArgumentCaptor for stubbing? - Stack Overflow)
Als zweiten Test ruft A (Mein UnitTest) nun B (meine eigentliche Funktion) mit einem ungültigen Wert auf und gibt eine Exception an A zurück. Nun kann ich bei A schauen ob eine Exception gekommen ist und das C nicht aufgerufen wurde.[/quote]
nach deinen vorigen Aussagen mockt man C immer - und das ist eben falsch. Ansonsten ist mir wohl bekannt wie ein Testablauf aussieht.

falsch. Auf die Frage was zu testen ist mit „alles“ zu antworten, egal in welchem Kontext, ist schlichtweg falsch.

Alles … Wenn die Unittests nur 15-20% der zeit brauchen, dann ist das auch problemlos zu vertreten. Intergrationstests dürften ja dann deutlich teurer sein.

[QUOTE=bygones]nach deinen vorigen Aussagen mockt man C immer - und das ist eben falsch. Ansonsten ist mir wohl bekannt wie ein Testablauf aussieht.
[/QUOTE]
Das Thema mocking bezog sich bei meinen Posts auf dieses Problem und von immer war nicht die rede, sondern für dann wenn es Sinnvoll ist, wie in diesem Fall um den es ging. Wobei es häufig von Vorteil ist, da ich mit einem Unittest, wie der Name schon sagt, häufig eine Unit testen will. Wenn ich die Integration von verschiedenen Units/Komponenten/Systemen testen will, nehme ich dafür üblicherweise dann einen Integrationstest, wobei das auch wiederum in Java zum Teil mit jUnit-Test gemacht werden, kann. Wodurch die Bezeichnung Unit-test schon leicht schwammig wird.

[QUOTE=bygones;126816]
falsch. Auf die Frage was zu testen ist mit “alles” zu antworten, egal in welchem Kontext, ist schlichtweg falsch.[/QUOTE]
Ok, ja, schlecht formuliert, wobei ich da der Meinung bin, man sollte nun mal bei einer Unit jegliche Funktionalität testen, somit alle public Methoden einer Klasse die nach außen gereicht werden, damit ich für meine Unit sicher bin, das dort alles funktioniert. Und da diese Unit üblicherweise klein geschnitten sein sollte, sollte dafür auch der Aufwand nicht allzu hoch sein. Aber ja ansonsten sollte man Analysieren wie viel Aufwand ist für welches Risiko zu tun. Und da stimme ich dir überein, das Risiko nicht einfach die Häufigkeit des Aufrufes ist.

Aber vorher sollte definiert sein was ist denn Alles? Ist damit eine 100% Anweisungsüberdeckung gemeint oder 100% Entscheidungsüberdeckung, 100% Methodenaufrufe, 100% Usecases, …

Ja, dieses Fragezeichen habe ich auch immer über den Kopf. Aber auch bei Unittests habe ich Zweifel, dass 100% sinnvoll sein kann … habe aber momentan kein Beispiel parat.

das ging wohl dann etwas unter.

Ja fuer den Anfang mag das verwirrend sein, dass man für unterschiedliche Konzepte das gleiche Tool nimmt. Aber das macht die Konzepte an sich nicht schwammiger.

durch diesen Zusatz ist das Wort „alles“ schon relativiert. Ich wollte auch nur klarstellen, dass ein pauschales „alles“ in keinem Kontext korrekt ist und noch zusätzliche Information benötigt - ein Tester ist Skeptiker und pauschalantworten werden nie angenommen :wink:

100% coverage in unit tests sind nicht immer sinnvoll sich als Ziel zu setzen. Krasses Bsp: coverage = 100% aber 0 asserts im code.

Wie gesagt, arbeitest du alleine in dem Projekt so musst du die Antworten finden. Arbeitest du in einem Team und bist nicht in der Rolle, so gilt es denjenigen zu finden und zu fragen - bzgl Unitests ists aber eher besser einen senior Entwickler zu fragen statt gleich die Management ebene

naja 100% Branchcoverage sind nicht immer möglich z.B.

if (a!= null && a.getValue()>10){

Da können nicht alle möglichen Kombinationen getestet werden.

So haben wir bei uns im Projekt als Bodenlinie mindestens 65% Branchcoverage (wenn das nicht erreicht wird ist der Build rot[gefailed]) und das Projekt liegt aktuell bei ca 80%

Dass in dem Thema innerhalb von 2 Stunden knapp 2 Seiten voll wurden, deutet etwas an :wink:

Die Spanne ist offensichtlich sehr breit. Von „Wenn’s compiliert wird’s schon auch laufen (und wenn nicht, wird uns der Kunde schon sagen, wo’s hakt)“, über ein paar pragmatische Tests der wichtigsten public-Methoden und TDD, bis zu einem pauschal-unreflektieren „alles“ (mit ausuferndem Aufwand).

Das erste machen wir auf der Arbeit. Das zweite versuche ich, zuhause zu machen. Zu TDD kann ich nicht viel sagen, könnte nur ein paar Gründe nennen, warum ich es (! soweit ich es verstehe, bzw. das, was ich darunter verstehe !) für nicht immer anwendbar oder praktikabel halte. Das „alles“ wurde schon als falsch dargestellt, aber es sollte klar sein, dass das, um es als falsch oder richtig bewerten zu können, genauer ausdifferenziert werden muss. Natürlich wäre es gut, wenn „alles“ getestet würde - im Sinne von 100% Testabdeckung. Andernfalls muss man immer fragen, warum es den Code, der beim Testen der public API nicht zumindest implizit verwendet wird, überhaupt gibt, oder (wenn er verwendet werden könnte), warum er nicht getestet ist, bzw. ob er überhaupt richtig funktioniert.

Ich persönlich halte die angedeuteten 10-15% für deutlich zu wenig, wenn man wirklich das Verhalten, das (hoffentlich ja) in der API-Doc der public-Methoden dokumentiert ist, konsequent abtesten will. Paradoxerweise kann es sogar so sein, dass der Testaufwand deutlich höher wird, wenn man „besseren“ Code schreibt. Etwas vereinfacht: Schon für vermeintlich einfache Methoden können mehrere Tests notwendig sein - und ggf. ähnlich viele, wie für eine (komplizierte) Methode.

Ich hatte schon gelegentlich auf die Google Collections Unit Tests verwiesen, die deutlich machen, wie absurd aufwändig gewissenhafte Tests tatsächlich sind. Es fällt mir schwer, mir vorzustellen, dass man seinem Chef sagt: „Ja, das Programm läuft und funktioniert“, und man dann 4 Wochen später sagt: „Ja, jetzt ist es noch der gleiche Stand wie vor 4 Wochen, aber jetzt habe wir auch Unit-Tests“.

Vermutlich haben von den 7 (!) Leuten, die unten in der „users browsing this Thread“-Liste stehen, schon weitere geantwortet. Diese Antwort ist aus dem Stand von Post #33 :wink:

[quote=bygones]nach deinen vorigen Aussagen mockt man C immer - und das ist eben falsch.[/quote]das ist richtig, denn wenn C nicht gemock wird ist es kein Unit-Test mehr, sondern ein Integrationstest.

[HR][/HR][quote=Greta;126799]Wie Unregistered auch schon sagt, ist doch super wenn es überall knallt nach der Änderungen.[/quote]Nein, ist es nicht, weil damit zwar der Fehler zu tage tritt, aber du noch nicht weißt wo.
Wenn Du einen “echten” UT hast, dass weist Du ohne debugging ganz genau wo der Fehler ist.

Außerdem machst Du Dir ja unötige Arbeit, wenn Du zig UTs ändern musst, obwohl Du nur eine kleine Änderng in deinem BLL hattest.

[HR][/HR][quote=maki;126801]Wie bereits von anderen erwaehnt, ein isolierter Unit Test testen nur eine Methode einer Klasse, sonst nix.[/quote]Es ist sogar viel krasser: ein UT testet genau eine Anforderung an eine Unit. Dabei muss noch nicht mal auf eine Klasse/Methode eingeschränkt sein (obwohl das natürlich letztendlich genau darauf hinaus läuft).

[HR][/HR][quote=freezly;126823]Ja, dieses Fragezeichen habe ich auch immer über den Kopf. Aber auch bei Unittests habe ich Zweifel, dass 100% sinnvoll sein kann … habe aber momentan kein Beispiel parat.[/quote]Wenn der Aufwand für’s mocken der Anhängigkiten zu groß wird, sollte man aufhören (und womöglich refactorn). IdR trifft das beim GUI-Layer zu.

[HR][/HR][quote=Marco13;126829]über ein paar pragmatische Tests der wichtigsten public-Methoden und TDD, bis zu einem pauschal-unreflektieren “alles” (mit ausuferndem Aufwand).[/quote]Wobei TDD eben auch “alles, aber ohne ausufernden Aufwand” bedeutet…

[HR][/HR][quote=Marco13;126829]Paradoxerweise kann es sogar so sein, dass der Testaufwand deutlich höher wird, wenn man “besseren” Code schreibt. Etwas vereinfacht: Schon für vermeintlich einfache Methoden können mehrere Tests notwendig sein - und ggf. ähnlich viele, wie für eine (komplizierte) Methode.[/quote]Dem gegenüber steht aber die Sicherheit, versehentliche Änderungen sofort zu bemerken, die deutlich bessere Struktur des Produktiv-Codes und damit dessen bessere Wiederverwendbarkeit.

Ja, wenn man TDD man ist man die meiste Zeit am Tests schreiben, dafür muss man viel weniger Fehlersuche betreiben.

[HR][/HR][quote=Marco13;126829]sagt: “Ja, das Programm läuft und funktioniert”, und man dann 4 Wochen später sagt: “Ja, jetzt ist es noch der gleiche Stand wie vor 4 Wochen, aber jetzt habe wir auch Unit-Tests”.[/quote]Das ist nur ein Problem, wenn man nicht TDD praktiziert.
Unittests im Nachhinein schreiben ist die Hölle, ins besonderen wenn die Entwickler des produktiven Codes solche Dinge wie DI, KISS, SoC, SRP und LoD ignorieren, was bei TDD schon aus Faulheit heraus nicht passiert.

bye
TT

Aus meinem Verständnis ging nirgends hervor, dass C per se eine zu mockende Abhängigkeit ist. Wenn C teil der Klasse ist, oder durch eine real Implementierung bereitgestellt werden kann, so sollte das getan werden. Ich persönlich sehe zu oft wie Leute mit mocks um sich schmeissen, weil man „ja alles isoliert haben will“.

*** Edit ***

Verweis auf meine Signatur „Test Driven Development is like sex. If you dont like it, you probably aint doing it right“ :wink:

Unittesting hat noch nicht viel mit Code Qualität zu tun. Vor allem bei Test afterwards ist der Zusammenhang vernachlaessigebar. Qualitativ guter Code lässt sich leichter testen, sagt aber nix aus, wieviele Tests er benötigt.

Auch die Anzahl der Tests sind meiner Ansicht nach nicht ein grosser Beitrag zum Aufwand. Bei qualitativ gutem Code sind diese mehr Methoden schnell geschrieben. Aufwand wird’s dann wenn die Testmethoden hart zu implementieren sind

Wenn ich in einem Projekt nach den Qualitätsmerkmalen frage kommt immer wieder an „ja gut, haben Unit tests“…

Das möchte ich mal hervorheben ! Unit testing heisst nicht per se ich teste einzelne Methode.

Wenn man selbst Test schreiben als Belastung sieht bzw nicht den Mehrwert, so kann man das gewiss auch nicht seinem Chef erklären.

Naja nur weil ich schon meine Implementierung für den Datenbankzugriff habe muss ich aber nicht unbedingt in jedem Unittest die komplette Datenbankanbindung verwenden, sonder besser einen Mock von dieser, damit ich, wie der Name schon sagt die Unit und nicht die Integration teste (also bei Unit tests, bei Integrationstests sieht das natürlich anders aus) Da ansonsten weil doch ein Fehler in der Datenbankanbindung ist, mein test für das ändern eines Namens auf die Nase fliegt,… Ich will bei einem Unittest schließlich nur meine Unit testen und nicht das komplette Sytstem. Genau aus diesem Grund gibt es ja Unit-tests, Integrationstests,… wie man sie aus V und W Modell kennt. Wenn ich das alles nicht will, kann ich auch einfach am ende ein wenig auf der Anwendung rum clicken und schauen ob alles passt :-p

wir reden das selbe nur geschickt aneinander vorbei.

Natürlich sollen jegliche Verbindung zu externen Systemen etc getrennt werden. Wenn ich aber in meinem eigenen System bin, so ist ein Mock nicht unbedingt die Antwort. Mein Standpunkt ist dass man von dem Denken „Abhängigkeit = Mock“ weg soll und mock dann einsetzen sollte, wenn man ihn braucht, nicht weil man ihn einsetzen kann.

Google Testing Blog: Testing on the Toilet: Don’t Overuse Mocks

Are Too Many Mocks A Code Smell? | James Carr

weil ich endlich mal mitdiskutieren kann :slight_smile: Wer braucht schon Code fragen… entwicklerzeugs :wink:

[quote=bygones]Ich persönlich sehe zu oft wie Leute mit mocks um sich schmeissen, weil man “ja alles isoliert haben will”.[/quote]Dss ist aber genau richtig.
“Ein Unittest darf genau eine Urschae haben, warum er fehl schlägt” Sagt zumindest Roy Osherove.

[HR][/HR][quote=bygones;126832]“Test Driven Development is like sex. If you dont like it, you probably aint doing it right”[/quote]Das find ich gut, darf ich das verwenden?

[HR][/HR][quote=bygones;126832]Unittesting hat noch nicht viel mit Code Qualität zu tun.[/quote]Doch.
Wenn der Code schlecht ist, ist er auch schwierig zu testen. Wenn er schwierig zu testen ist wird er nicht getestst.

[HR][/HR][quote=bygones;126832]Vor allem bei Test afterwards ist der Zusammenhang vernachlaessigebar.[/quote]Tas Last ist keine Option.

[HR][/HR][quote=bygones;126832]sagt aber nix aus, wieviele Tests er benötigt.[/quote]Das spielt keine Rolle: Soviel wie möglich ist die Devise.

[HR][/HR][quote=bygones;126832]Das möchte ich mal hervorheben ! Unit testing heisst nicht per se ich teste einzelne Methode.[/quote]Aber eben “eine einzelne Anforderung”…

[HR][/HR][quote=bygones;126832]Wenn man selbst Test schreiben als Belastung sieht bzw nicht den Mehrwert, so kann man das gewiss auch nicht seinem Chef erklären.[/quote]Definitiv!

bye
TT

*** Edit ***

[quote=bygones;126834]dass man von dem Denken “Abhängigkeit = Mock” weg soll und mock dann einsetzen sollte, wenn man ihn braucht,[/quote]Man braucht die Mocks immer, weil die Verwendung der echten Abhängigkeiten die Unittest zu langsam macht.
In meinen Projekten Laufen 2000+ UT in 3 Sekunden. Schaffst Du das mit echter DB-Anbindung?

bye
TT