Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > de.comp.lang.java > #13013

Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle

From Wanja Gayk <brixomatic@yahoo.com>
Newsgroups de.comp.lang.java
Subject Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle
Date 2016-07-18 00:33 +0200
Organization Aioe.org NNTP Server
Message-ID <MPG.31f6261eb408512e9896be@news.aioe.org> (permalink)
References <du7ob7Fecu7U1@mid.individual.net> <1467923825.579226@alpaka.in-berlin.de> <MPG.31ea26fc280e77349896bc@news.aioe.org> <nlp8pd$js5$1@newsreader4.netcologne.de>

Show all headers | View raw


In article <nlp8pd$js5$1@newsreader4.netcologne.de>, Patrick Roemer 
(sangamon@netcologne.de) says...
> 
> Responding to Wanja Gayk:

> > Manchmal programmiert man ohne klares Ziel, weil das noch nicht in Sicht 
> > ist und man sich langsam voran tastet, bzw. baut man wärend der 
> > Implementation sehr oft noch sehr viel um, refactored eine Menge und man 
> > will dafür nicht immer die Tests komplett umbauen, weil sich die API 
> > ändert (weil man auf halbem Wege merkt, dass sie doch nicht so elegant 
> > war, wie man dachte und einem was besseres einfällt). 
> 
> Aber wie merkt man das denn ohne Tests (oder sonstigen Code der mit dem
> API arbeitet)? :)

Nicht jede Methode ist Teil einer öffentlichen API, sondern das meiste 
Zeug ist private (bzw. package private) oder Teil einer groben Idee.

Beispiel: Ich will ne Liste von Objekten zusammen stellen, die sich aus 
irgendeinem Zustand ergibt (Zeilen aus einer tabelle, Artikel, bestimmte 
Objekte aus einer größeren Masse von Objekten, egal). 
Du machst also ne einfache Methode, die sieht so aus:

List<Something> getSomethings(){...}

Schön, sieht erstmal richtig aus, sieht auch nicht so schwer aus, tut 
wahrscheinlich was es soll, man freut sich. Werde da bei Gelegenheit 
einen Test für schreiben, um sicher zu gehen, aber jetzt geht's erstmal 
weiter mit dem Code, der das dann benutzt,vielleicht fällt dann ja was 
auf.

Dann merke ich: Nah, also eine Liste zu liefern war irgendwie blöd, weil 
ein Set da klarer wäre, obwohl: wenn ich die Duplikate sehen will, wäre 
es sinnvoll eine Liste einem Set gegenüber zu stellen.. will ich 
duplikate loggen oder nicht? Will ich vielleicht ne LinkedList haben, 
und billig vorne was einfügen können? Weiß ich noch nicht so genau, aber 
es erscheint mir ne gute Idee zu sein, wenn ich erstmal eine Collection 
im Parameter mitgebe, dann kann ich mir später als Aufrufer aussuchen, 
was für eine Collection ich befüllen will.
Also wird daraus:

<T extends Collection<? super Something>> T getSomethings(T target){...}

Schön, sieht richtig aus, mache ich dann einen Test für, wenn es mir 
passt, erstmal weiter gehen..

Dann merke ich: Okay, hier benutze ich das Stück nochmal, aber das hier:

Set<Something> someThings = getSomethings(new HashSet<Something>());
Set<OtherThings> otherThings = new HashSet<>();
for(Something s : someThings){
 otherThings.add(toOtherThing(s));
}

...ist irgendwie blöd jetzt, ich packe alles in meine Collection und 
muss dann nochmal drüber iterieren, um es auf was anderes zu mappen, 
besser ich gebe der Methode nicht nur Zielcollection mit, sondern 
zusätzlich eine Mapping-Funkction dann muss ich nur einmal drüber 
iterieren und der Aufrufer, der das schon benutzt, der übergibt einfach 
ne Identity-Funktion und ist fein raus, oder ich mache ihm eine neue 
Methode mit der alten Signatur, die einfach weiter delegiert und eine 
Identity-Funktion als Parameter übergibt, damit wird der Aufrufer 
lesbarer:
Also wird daraus:

<T extends Collection<? super Something>> T getSomethings(T target){
 return getSomethings(target, Function.identity());
}
<U, T extends Collection<? super U>> T getSomethingsFunction<? super 
Something, U> mapper, T target){...}

Und der Code oben wird zu:
Set<OtherThings> otherThings = getSomethings(myClass::toOtherThing, new 
HashSet<OtherThings>());

Hätte ich diese Entwicklung "test first" geschrieben, also mit allen 
Corner cases, hätte ich jeden der Tests für diese Methode (und das kann 
ein ganzer Batzen sein) zweimal umschreiben müssen. So sehe bin ich bei 
etwas angekommen, wo ich denke: okay, das sollte reichen.
Entweder schreibe ich jetzt einen Test, oder wenn mich noch das 
"höhere" Ziel quält, wegen dem ich diese Methode überhaupt geschrieben 
habe, kann es sein, dass ich das erst zuende machen muss, um den Faden 
nicht zu verlieren.
Also mache ich mit meiner Idee erstmal weiter, bis ich zu dem Punkt 
komme, wo ich denke dass es jetzt "soweit funktionieren sollte, dass ich 
es testen kann", weil ich keine größeren Änderungen mehr erwarte und 
dann schreibe ich Tests. Zunächst für das grobe Zeug, dann für das 
feinere Zeug. Ich präzisiere dann meine Erwartungen immer genauer und 
die Tests sagen mir, ob ich richtig liege, oder einen Hund drin habe. 

Meine Entwicklung sieht in der Regel so aus:
1.Hack
2.Refine
do{
 3.Write Test
 4.Fix Bugs
}while (bugs detected || tests incomplete)

Üblicherweise beschränkt auf ein System von 1 bis 4 Klassen, bzw. auf 
ein bis drei Milestones auf meiner Todo-Liste.

Ein Test zurrt mir die API einer Methode fest, macht Änderungen teurer, 
schränkt mich ein. Derart Entscheidungen verschiebe ich gerne auf 
später.
Ein Reihe Tests, die ich nach einem halben Tag wieder löschen kann, weil 
ich merke, dass ich auf dem Weg eine bessere Lösung für ein Problem 
gefunden habe ("oh der Workaround ist eigentlich ziemlich cool, da 
brauche ich diese 5 Methoden eigentlich gar nicht mehr.."), ist 
verschwendete Zeit, die verbringe ich lieber am Problem oder in der 
Teeküche, um den Kopf wieder frei zu kriegen.

> Natürlich lege ich auch erst mal explorativ los, und schreibe ganz
> bestimmt nicht erst mal für jede Zeile "Produktivcode" einen Test, der
> diese erzwingt. Aber ich will relativ bald ein Gefühl dafür bekommen,
> wie sich so ein API anfühlt - und den explorativen Code dafür schreibe
> ich doch lieber gleich als Testcases. 

Letzteres ist bei mir high level. Beispiel: Ich brauche einen REST Web 
Service, der mir irgendwelche Kunden zurück liefert. Der Test ist 
einfach: HTTP connection aufmachen, Kundennummer rein geben, kommt der 
Kunde zurück? Stimmen die Fehlermeldeungen? Das will ich ja nicht jedes 
mal immer neu ausprobieren und mit dem Browser oder CURL rumlutschen, da 
soll einmal Knöpfchen drücken genügen. Also Test schreiben, dann 
implementieren. Da weiß ich, was ich will, im voraus.

Und dann hast du Aufgaben, wie: Ich suche alle Files in einem Directory, 
extrahiere daraus irgendwelche Daten, cache die, ggf. muss ich per 
Timestamps prüen, ob sich was in einem File geändert hat, damit ich den 
neu einlese, etc. Da ergeben sich Fragen, wie: Wie halte ich die Daten, 
wie die Relation der Daten zu den Files und den Timestamps, bzw. brauche 
ich read-write-locks, was passiert, wenn ein File gelöscht wird, oder 
whatever.. und da weiß man ggf. noch nicht ganz genau, was man will und 
was das Sinnvollste ist. Mit anderen Worten wird erstmal explorativ 
drauflos gehackt und geschaut, wie sich das verhält und ob man das so 
nehmen kann, oder ob was anderes besser wäre, etc. und der einzige 
"Test" bis dahin hat die Qualität einer hingerotzen Main-Methode, nur um 
das Zeug auf dem höchsten Level auszulösen. Keine Corner cases, etc.
Und wenn ich dann ein Gefühl dafür habe, wohin die Reise geht, was ich 
will und dass die Idee, die ich da ausformuliert habe gut ist, sodass 
jetzt der Feinschliff kommen kann, dann fange ich an top down die Tests 
zu schreiben, um a) die API zu fixieren, b) vergessene corner cases zu 
entdecken und c) sicher zu gehen, dass ein späterer Bugfix kein anderes, 
erwartetes Verhalten bricht.

> Wenn das API sich zu stark ändert,
> schmeisse ich die entsprechenden Tests lieber weg anstatt zu
> refaktorieren und schreibe neue.

Ich fand heute diesen Talk hier relativ interessant (okay, ich bin 
schuldig mir sowas auf nem Sonntag anzuschauen, weil es interessanter 
ist das das TV-Programm). Zufällig drüber gestolpert. Ich war 
überrascht, dass der Abschnitt von Minute 15 bis Minute 28 "Spike and 
Stabilize" meiner Art Software zu entwickeln sehr nahe kommt:
https://www.youtube.com/watch?v=USc-yLHXNUg

Gruß,
-Wanja-


-- 
..Alesi's problem was that the back of the car was jumping up and down 
dangerously - and I can assure you from having been teammate to 
Jean Alesi and knowing what kind of cars that he can pull up with, 
when Jean Alesi says that a car is dangerous - it is. [Jonathan Palmer]

Back to de.comp.lang.java | Previous | NextPrevious in thread | Next in thread | Find similar


Thread

Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-07 21:19 +0200
  Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle Peter <peter@localhost.com> - 2016-07-07 22:37 +0200
    Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle Michael Paap <feunews@mpaap.de> - 2016-07-07 23:21 +0200
      Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-08 14:09 +0200
    Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle Wanja Gayk <brixomatic@yahoo.com> - 2016-07-08 22:10 +0200
      Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle Patrick Roemer <sangamon@netcologne.de> - 2016-07-09 00:17 +0200
        Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle Wanja Gayk <brixomatic@yahoo.com> - 2016-07-18 00:33 +0200
          Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-18 01:01 +0200
          Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle Patrick Roemer <sangamon@netcologne.de> - 2016-07-19 10:58 +0200
            Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle Wanja Gayk <brixomatic@yahoo.com> - 2016-07-21 00:07 +0200
          Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-19 14:09 +0200
  Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle Patrick Roemer <sangamon@netcologne.de> - 2016-07-08 00:28 +0200
    Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-08 14:13 +0200
  Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-08 16:05 +0200
    Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle Wanja Gayk <brixomatic@yahoo.com> - 2016-07-08 22:35 +0200
    Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle Patrick Roemer <sangamon@netcologne.de> - 2016-07-09 00:01 +0200
      Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-11 00:40 +0200
        Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-15 23:25 +0200
    Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-15 00:12 +0200
  Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle Wanja Gayk <brixomatic@yahoo.com> - 2016-07-08 21:57 +0200
  Re: Unit-Tests von Einheiten ohne öffentliche Leseschnittstelle "Christian H. Kuhn" <qno-news@qno.de> - 2016-07-11 01:13 +0200

csiph-web