2016-06-23 63 views
0

我有一個非常簡單的類,異步寫入列表保存到文件:TestNg多線程問題。 TestNG的不尊重子線程

import java.io.FileWriter; 
import java.io.IOException; 
import java.lang.reflect.Type; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.atomic.AtomicInteger; 

import com.google.common.reflect.TypeToken; 
import com.google.gson.Gson; 

public enum FileOps { 
    INSTANCE; 

    private ExecutorService threadPool = Executors.newFixedThreadPool(30); 
    private AtomicInteger fileCount = new AtomicInteger(0); 

    private <T> void writeListToFile(String fileName, List<T> obj) { 
     FileWriter writer = null; 
     Type tType = new TypeToken<ArrayList<T>>() { 
      private static final long serialVersionUID = 4376511240656742709L; 
     }.getType(); 
     Gson gson = new Gson(); 
     try { 
      writer = new FileWriter(fileName); 
      writer.append(gson.toJson(obj, tType)); 
      writer.flush(); 
     } catch (Exception e) { 

     } finally { 
      try { 
       writer.close(); 
      } catch (IOException e) { 
      } 
     } 
    } 

    public <T> void asynWriteListToFile(List<T> obj){ 
     threadPool.execute(new Runnable() { 
      @Override 
      public void run() { 
       String fileName = "C:\\data\\" + fileCount.incrementAndGet() + "_data.txt"; 
       System.out.println(fileName); 
       FileOps.INSTANCE.writeListToFile(fileName, obj); 
      } 
     }); 
    } 

} 

我已經寫了單元測試

import java.util.ArrayList; 
import java.util.List; 

import org.testng.annotations.Test; 

public class FileOpsTest { 

    @Test 
    public void asynWriteListToFile() { 
     List<Integer> list = new ArrayList<>(); 
     list.add(3); 
     for (int i = 0; i < 10000; i++) { 
      FileOps.INSTANCE.asynWriteListToFile(list); 
     } 

    } 
} 

我這個類用TestNG

有一個奇怪的情況。在我的TestNg執行中,一些測試引擎如何不等待子線程完成。所以我期望在磁盤上寫入10000個文件,但每次看到寫入磁盤的文件較少。但是,如果我使用主要方法編寫客戶端,一切正常。

import java.util.ArrayList; 
import java.util.List; 

public class FileOpsClient { 

    public static void main(String[] args) { 
     List<Integer> list = new ArrayList<>(); 
     list.add(3); 
     for (int i = 0; i < 10000; i++) { 
      FileOps.INSTANCE.asynWriteListToFile(list); 
     } 
    } 
} 

不知何故testNg引擎關閉我的線程池。

回答

2

由於文件異步寫入,FileOpsTest.asynWriteListToFile()結束前的文件都寫和org.testng.TestNG(或IDE的測試運行)調用System.exit(int)(例如TestNG.java:1375)。

相比之下,FileOpsClient.main(String[])沒有顯式調用System.exit(int),因此JVM會等待您的線程結束,因爲它們不是守護進程線程。有關更多詳細信息,請參閱How to make TestNG wait for my test to complete before shutting it down

在你的情況下,你可以做一些修改,讓測試可以有效地調用threadPool.awaitTermination(long, TimeUnit)(如製作FileOps.threadPool「包本地」而不是「私人」,並從你的測試訪問它,添加上FileOps的方法做所以爲你保留FileOps.threadPool「私人」等)。

但是,如果你的目標是單元測試的話,我會建議一個ExecutorService是一個「尷尬的合作者」,那麼你應該重構你的代碼,使1)你可以使用測試產卵線程嘲笑ExecutorService(見How to unit test that ExecutorService spawns new thread for task?) ,2)你可以測試一個列表的實際寫入文件的邏輯,與創建異步任務的方式無關,以及3)不直接使用FileWriter,而是簡單地使用Writer,這樣你在測試時也可以進行模擬並避免實際在單元測試中寫入/讀取文件,並在需要時將這種練習留給集成測試。

+1

批評人士不直接使用FileWriter,而是將抽象Writer類型注入到模擬方法中,而對於更多可測試的代碼更改方式則改變了單元測試的方式。這是一個頓悟。謝謝。 – cgon