2010-10-28 36 views
6

在多線程環境中使用JUnit時遇到了一個問題。下面的代碼應該會失敗,但它實際上在eclipse中傳遞。在多線程環境中使用JUnit時出現的奇怪問題

public class ExampleTest extends TestCase { 

    private ExecutorService executor = Executors.newFixedThreadPool(10); 

    private volatile boolean isDone = false; 

    public void test() throws InterruptedException, ExecutionException { 
     executor.submit(new Runnable() { 

      @Override 
      public void run() { 
       try { 
        fail(); 
       } finally { 
        isDone = true; 
       } 
      } 
     }); 

     while (!isDone) { 
      Thread.sleep(1000); 
     } 
    } 
} 

這裏還有一段代碼,在這裏我使用Future.get()來等待線程停止,在這種情況下它會失敗。

public class ExampleTest extends TestCase { 

    private ExecutorService executor = Executors.newFixedThreadPool(10); 

    private volatile boolean isDone = false; 

    public void test() throws InterruptedException, ExecutionException { 
     Future future=executor.submit(new Runnable() { 

      @Override 
      public void run() { 
       try { 
        fail(); 
       } finally { 
        isDone = true; 
       } 
      } 
     }); 

     future.get(); 
    } 
} 

我GOOGLE了一下,發現JUnit的不能處理多線程的單元測試,但什麼是這兩段代碼之間的區別是什麼?謝謝

回答

6

JUnit無法看到除運行測試的線程之外的線程中發生的異常。在第一種情況下,通過調用fail發生異常,它發生在由executor運行的單獨線程中。因此它對JUnit不可見並且測試通過。

在第二種情況下,在由executor運行的單獨線程中發生相同的異常,但在調用future.get時,異常會有效地「報告回」測試線程。這是因爲如果未來的計算由於任何異常而失敗,則future.get將拋出ExecutionException。 JUnit能夠看到這個異常,因此測試失敗。

+0

那麼,有沒有在這種情況下替換Junit? – zjffdu 2010-10-28 07:25:45

0

看看http://www.youtube.com/watch?v=wDN_EYUvUq0(從17:09開始),它解釋了你可以用JUnit和線程得到的問題。

我認爲,在你的情況下,get()拋出ExecutionException,這就是爲什麼第二次測試失敗。在第一個測試用例中,jUnit沒有看到異常。

0

還有一個有趣的事實是Eclipse和IDEA可以在他們的junit測試運行器中產生一個虛擬機,並最終調用system.exit()。這意味着如果您在測試中沒有正確地等待(例如您在上面睡覺並希望任務已完成),則它可能會意外退出。有趣的,但不完全是你問的!

看到這個link的詳細信息...

1

正如@ abhin4v指出,在新的線程中的異常被吞噬。您可以嘗試提供自己的fail - 與頂級線程同步的方法,非常類似於您的示例get()

但是沒有必要使用Futures,只需寫入共享變量,指示失敗並使用newThreadId.join()。除此之外,我還沒有意識到在普通的JUnit中解決這個問題的其他方法。

2

@zjffdu As @ShiDoiSi指出,如果你有一個你想斷言或失敗的工作線程,Thread.join()可以正常工作。

如果你有多個工作線程,或者如果你想多一點方便,還有用於執行多線程的斷言JUnit的一個擴展:ConcurrentUnit

public class ExampleTest extends ConcurrentTestCase { 
    private ExecutorService executor = Executors.newFixedThreadPool(10); 

    public void test() throws Throwable { 
     executor.submit(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        threadFail("Failure message"); 
       } finally { 
        resume(); 
       } 
      } 
     }); 

     threadWait(); 
    } 
} 

好運