ThreadLocal und JUnit Parallelized runner
-
Servus,
ich versuche gerade einen JUnit-Test mit 4 Test-Methoden dahin zu kriegen, dass er parallel und parametisiert läuft.
Dazu gibt es http://hwellmann.blogspot.de/2009/12/running-parameterized-junit-tests-in.html . Der funktioniert an sich sehr gut und bietet die gewünschte Funktionalität.
Sofern ich ihn verstanden habe instanziert er die Testklasse für jeden Parameter in der Liste, die von der @Parameters Funktion returnt wird.
Gebe ich in der @Parameters Funktion als 4 Werte zurück und mein Klassen-Konstruktor akzeptiert solche Werte, so wird die Klasse 4 mal instanziert und nochdazu parallel mit den anderen Instanzen auf einem eigenen Thread ausgeführt.
Nun habe ich eine Variable, die static sein muss, da sie nachdem die Klasse fertig ist mit @AfterClass mit gewissen Funktionen aufgeräumt werden muss.
Allerdings muss diese Variable ebenfalls für die einzelnen Instanzen der Klasse einzigartig sein, denn sonst kommen die sich in die Quere.
Dazu bietet sich dann wohl ThreadLocal<MeineVariable> an.
Nun dachte ich mache ich folgendes :
- static ThreadLocal<MeineVariable> sMeineVariable definieren.
- in dem Konstruktor von MeineKlasse(wert value) überschreibe ich die initialValue methode von ThreadLocal, sodass sie mir Instanzen von MeineVariable abhängig von value zurück gibt.Im reslichen Code verwende ich dann sMeineVariable.get().methode.
Und in @AfterClass kann ich nun wunderbar sMeineVariable.get().cleanUp() aufrufen, da ja static.Habe ich die Grundfunktionalität von ThreadLocal so richtig verstanden? Denn darum geht es mir erstmal.
Das Resultat ist leider Murks, es werden irgendwie zuviele Instanzen von MeineVariable erstellt.Wenn der Grundgedanken stimmt, dann gehe ich gerne wesentlich mehr ins Detail, worum es eigentlich geht.
Danke
-
Kurzes Debugging ergibt, dass ich wohl missverstanden habe, wie oft der Konstruktor aufgerufen wird.
Sieht so aus als wäre das 8 mal und nicht 2 mal. Das regt den Verdacht, dass der Runner für jede Test-Methode eine Instanz der Klasse erstellt.
Puh ... das muss ich mir noch mal genauer anschauen.
-
Also es funktioniert reibungslos, wenn ich die Erzeugung von @BeforeClass und @AfterClass in jeweils @Before und @After auslagere.
Das hat natürlich den overhead zur Folge, dass MeineVariable vor jeder einzelnen Test-Methode neu erzeugt und danach gelöscht wird, anstatt nur mit jeder Klassen-Instanz einmal erzeugt und gelöscht zu werden.
Bin relativ ratlos, was mein eigentliches Ziel betrifft. Sollte ja technisch irgendwie möglich sein.
Aber wenn das zu weit führt, dann halt mit dem overhead. Was soll man machen ...
-
Muss hier jetzt leider noch einen dritten Post dranhängen.
Ich habe die letzte, mir erdenkliche Möglichkeit ausprobiert:
Ich deklariere ThreadLocal<MeineVariable> als static member-Variable meiner Klasse.
Hier definiere ich ThreadLocal so, dass initialValue entsprechend überschrieben wird. Also so :
private static ThreadLocal<MeineVariable> meineVariableThreadLocal = new ThreadLocal<MeineVariable>() { @Override protected MeineVariable initialValue() { return new MeineVariable(params); } };
Zusätzlich habe ich noch eine "Normale" Version von MeineVariable als Member definiert:
private MeineVariable meineVariable;
Im Konstruktor lasse ich diese unberührt.
In @Before mache ich folgendes:
meineVariable = meineVariableThreadLocal.get();
Dann verwende ich im restlichen Code einfach
meineVariable.funktion()
.Damit am Ende auch aufgeräumt wird, mache ich dann in @AfterClass:
meineVariableThreadLocal.get().cleanUp()
Das Ganze funktioniert zwar insofern, dass jetzt die korrekte Anzahl an Instanzen von MeineVariable erzeugt wird ( da ThreadLoca.get() nur in dem Falle, dass es noch kein Objekt besitzt die initialValue() methode aufruft, um eins zu erzeugen) hat allerdings den negativen Effekt, dass am Ende nur eins von den zwei MeineVariablen Instanzen mit .cleanUp() wieder aufräumt wird.
Das ist sehr verwirrend. Einerseits scheint die eindeutige Zuweisung zu den Instanzen zu funktioniert (Das kann ich beobachten, jeder Thread benutzt die korrekte MeineVariable-Instanz) allerdings scheint am Ende (@AfterClass) keine Unterscheidung mehr möglich zu sein.
Hmmmmm.
Falls hier niemandem mehr was auffällt und auch ich keinen Geistesblitz habe, so denke ich werde ich wohl mit dem Overhead klarkommen müssen. Besonders zeitkritisch ist das Ganze eh nicht.
Dennoch stört es mich, dass ich dieses Umsetzung technisch nicht lösen kann.