2013-05-17 7 views
13

Ich versuche, einen Komponententest zu schreiben, der mehrere Threads erfordert. Es scheint jedoch, dass die Threads nur einen Teil der Ausführung stoppen. Betrachten Sie den folgenden Code ein:Thread verhält sich seltsam in JUnit

public class Test { 
    @org.junit.Test 
    public void TestThreads() { 
     new Thread(new Runnable() { 
      public void run() { 
       for (int i = 1; i < 1000; i++) System.out.println(i); 
      } 
     }).start(); 
    } 
} 

Wenn ich diesen Unit-Test ausführen, wird es in der Regel stoppen Ausgabe irgendwo zwischen 140-180 anzeigt. Wenn ich diesen Code in eine reguläre Klasse umwandeln und ausführen, funktioniert es einwandfrei. Hat jemand eine Idee, was ich hier vermisse?

Danke, - Andrew.

Antwort

0

Es ist wahrscheinlich, dass der übergeordnete Thread, der den Test ausführt, beendet wird. Wenn Sie es vermeiden können, dann erstellen Sie keinen neuen Thread in Ihren Tests. Ich würde eine neue Klasse erstellen, die das Runnable implementiert, und dann diese Klasse testen, indem ich einfach auf dieses Objekt rufe. Sie sollten die Thread-Planung nicht als Teil Ihrer Tests testen müssen. Hoffentlich hat das Java-Sprachentwickler-Team das abgedeckt :)

6

In der TestThread-Funktion kann JUnit nicht sagen, dass Sie einen neuen Thread erstellt haben. Sie ruft alle Objekte zurück, die sie in der Funktion erstellt hat, sobald die Funktion endet (die letzte Zeile ist erreicht) und nichts über den Thread weiß.

Aus diesem Grund sehen Sie Ausgabe vom Thread, bis es getötet wird. Wenn Sie auf Thread Completion warten möchten, können Sie Thread.join verwenden (direkt nach dem Aufruf Thread.start()), um es auf das Ergebnis warten oder wait-notify für ein Objekt verwenden.

auch gefunden, diese Frage: JUnit terminates child threads

+0

Zu sagen, dass JUnit Objekte löscht, ist ein wenig irreführend, da es in Java keine Möglichkeit gibt, ein Objekt explizit zu löschen. –

+0

@Martin rechts. JVM kann jedoch (GC) zurückfordern, wenn das Objekt nicht mehr benötigt wird. Sobald die Tests abgeschlossen sind, tötet JUnit Threads und im Grunde die gesamte JVM. – goblinjuice

+0

@goblinjuice das bedeutet nicht, dass JUnit überhaupt eine Objektinstanz abstürzt. Da die JVM beendet wird, wird jedes einzelne verwendete Speicherbit freigegeben. –

22

Sie können Thread.join() verwenden Sie den Test aus dem Finish zu verhindern, bevor der neue Thread seine Aufgabe erfüllt hat:

@org.junit.Test 
public void TestThreads() throws InterruptedException { 
    Thread t = new Thread(new Runnable() { 
     public void run() { 
      for (int i = 1; i < 1000; i++) System.out.println(i); 
     } 
    }); 
    t.start(); 
    t.join(); 
} 

Normalerweise wird die JVM beendet, wenn die letzte Nicht-Daemon-Thread wird beendet. Sie könnten erwarten, dass das Aufrufen von t.setDaemon(false) für den Thread verhindern würde, dass die JVM beendet wird, bevor die Aufgabe abgeschlossen ist. junit wird jedoch call System.exit(), wenn der Haupt-Thread beendet ist.

Wie Gus in den Kommentaren festhält: "Sie brauchen join() weil start() nicht blockiert".

Er ist richtig, dass Sie run() in diesem minimalen Beispiel auch aufrufen können. Ich nehme jedoch an, dass Sie einen Thread starten, weil Sie möchten, dass er gleichzeitig mit einem anderen Thread ausgeführt wird. Der Aufruf von run() auf einem Thread wird von FindBugs als möglicher Fehler gemeldet - wenn Sie nur run() aufrufen, möchten Sie wahrscheinlich nur Runnable implementieren, anstatt Thread zu verwenden.

+0

Seufz - schlag mich zum Schlag fair und quadratisch. –

+1

Es ist erwähnenswert in Ihrer Lösung, dass Sie Join() benötigen, da Start() nicht blockiert. IIRC, Sie könnten auch einfach Run() verwenden, wenn Sie nichts gleichzeitig machen wollen. – Gus

+0

@Gus mit 'run' auf einer' Thread' Instanz ist eine schlechte Idee. Warum?Weil 'run' nicht zu einem Thread, sondern nur zu einem einfachen Objekt macht. –

Verwandte Themen