2016-10-03 60 views
1

我在我的服務器上運行JUnit測試時遇到問題。當我在我的機器上運行測試時,根本沒有問題。當我在服務器上運行它時,「有時」會在我的所有服務器上出現故障。這意味着60%的嘗試有時會通過測試,40%的測試失敗。JUnit併發訪問synchronizedSet

我正在使用Mockito。我的測試從使用MessageListener嘲笑一些回覆開始,並將每個請求映射到響應,並且我正在使用線程安全的Collections.synchronizedSet(new HashSet<>())(我的synchronizedSet上的每個修改發生在synchronized(mySynchronizedSet){....})然後,我使用RestAssurd獲取響應特定的REST端點並聲明一些值。

當一個測試失敗,我看着Stacktrace,我看到我的一個映射(總是在同一個對象上)不起作用,並且在我的集合中這個特定的請求和響應之間沒有映射,我得到null請求這個端點。

我正在使用Jenkins自動編譯並運行測試,並且在失敗或我的Printlns上獲得堆棧跟蹤,否則沒有可用的調試工具。

這聽起來像是一個併發問題。我的意思是,我的收藏似乎沒有時間在RestAssurd請求端點之前做好準備。我測試過鎖,睡眠和另一個簡單的java併發解決方案,但它們並沒有幫助,而且這個問題的概率特徵導致我陷入了死衚衕。

每一個想法將不勝感激。

回答

2

根據你所說的話來判斷,似乎你對3種特定情況下的工作方式有誤解。

首先

,最明顯的,我即使提爲此道歉,但我在所有做的原因是因爲我收集,你還在學習(我道歉進一步,如果你」還沒有學習!以同樣的速度,你甚至可能沒有用我讀過的方式來暗示它,如果我誤讀了,那麼很抱歉):你不是在與詹金斯編譯,而是在用你擁有的任何JDK風格進行編譯(例如Oracle,Apple,GCJ等)。詹金斯是一個自動化工具,可幫助簡化您期望定期運行的繁瑣工作。我只提到這一點,因爲我知道現在的大學生在開放類中使用IDE,無法區分編譯器,運行時和IDE。通過使用一個線程庫

其次

,它不會自動讓你做固有線程安全的一切。請看下面的例子:

final Map<Object, Object> foo = Collections.synchronizedMap(new HashMap <>()); 
    final String bar = "bar"; 
    foo.put(bar, new Object()); 
    new Thread(new Runnable(){ 
     @Override 
     public void run(){ 
      foo.remove(bar); 
     } 
    }).start(); 
    new Thread(new Runnable(){ 
     @Override 
     public void run(){ 
      if(foo.containsKey(bar)){ 
       foo.get(bar).toString(); 
      } 
     } 
    }).start(); 

誰也不能保證第二個線程對#get(Object)呼叫前或第一線的號召,#remove(Object)後發生。考慮到

  1. 第二個線程可以調用#containsKey(Object)
  2. 那麼第一個線程獲得CPU時間,並呼籲#remove(Object)
  3. 那麼第二個線程目前擁有CPU時間,在這一點上呼籲#get(Object)

,從get(Object)返回的值將爲空,並且對#toString()的調用將導致NullPointerDereference。你說你正在使用Set,所以這個使用Map的例子主要是爲了證明一個觀點:僅僅因爲你使用的是線程安全的集合,並不會自動使你做線程安全的所有事情。我想像你正在做的事情與你的設置匹配這種行爲,但沒有代碼片段,我只能推測。

而且最後

您應該小心如何編寫JUnits。正確的JUnit測試就是所謂的「白盒」測試。換句話說,你知道測試中發生的一切,而且你明確地測試了被測試單元中發生的所有事情。被測單元只是您調用的方法 - 不是您的方法調用的方法,只有方法本身。這意味着,你需要一個好的模擬框架,並且模擬你的被測單元可能調用的任何後續方法調用。一些好的框架是JMockit,Mockito + PowerMock等。

這個的重要性在於你的測試應該測試你的隔離代碼。如果您允許網絡訪問,磁盤訪問等,那麼您的測試可能會失敗,它可能與您編寫的代碼無關,並且會使測試完全失效。在你的情況下,你提示網絡訪問,所以想象你的交換機/路由器/等存在一些吞吐量問題,或者你的網卡緩衝區已滿並且處理速度不夠快,無法執行你的程序。當然,失敗並不好,應該修復,但應該在「黑匣子」測試中進行測試。你的測試應該寫出來,以便消除這些問題的存在,並且只針對被測單元的特定方法測試你的代碼,除此之外別無它法。

編輯:其實我張貼的答案,關於白盒測試單獨討論可能有關:Is using a test entity manager a legitamate testing practice?

+0

尼斯的答案,但兩件事情:A)我不會連提Powermock對新手和B)牢記是的,JUnit主要用於「單元測試」;但是這個術語非常廣泛,許多人都在使用Junit進行任何測試,這很好。 JUnit只是一個工具,它適用於真正的單元測試,但也適用於任何其他類型的測試。 – GhostCat

+0

公平點。儘管PowerMock,儘管我不知道這些方法。 Whitebox測試你必須知道所有的東西,甚至開始正確地做到這一點,我會說Mockito + PowerMock比JMockit更馴服,所以我實際上更願意提到那一個。 Mockito + PowerMock的閱讀方式與英文相似,但JMockit真的很難閱讀和理解它是如何工作的,無論如何,我認爲。我發現那些沒有深入瞭解正確嘲諷細節的開發者最終會做出錯誤的單元測試,並且測試最終不會做他們認爲正在做的事情。他可能沒有選擇 – searchengine27

+0

事情是:當設計在錯誤的地方使用** static **時,需要Powermock;或**新**沒有適當的思維。換句話說:在編寫新代碼時,Powermock的需求直接轉化爲我眼中的「糟糕設計」。在觀看這些視頻後https://www.youtube.com/playlist?list=PLD0011D00849E1B79 ...我認爲我完全不需要Powermock(以及經常伴隨的各種小問題和大問題);而且自從幾個月以來,我並不後悔我完全停止使用它。 – GhostCat