2012-01-10 32 views
7

在審查我的代碼覆蓋率時,我注意到很多單元測試未能最終檢查嘗試關閉最終塊中打開的InputStreams的塊。單元測試最後在Java中的塊6

一個例子摘錄:

try { 
     f = new BufferedInputStream(new FileInputStream(source)); 
     f.read(buffer); 
    } finally { 
     if (f != null) 
      try { 
       f.close(); 
      } catch (IOException ignored) { 
      } 
     } 
    } 

有沒有合適的解決方案,檢查裏面的一切finally塊使用JUnit4?

我知道在保持最大生產率的情況下,無法實現100%的代碼覆蓋率。然而這些紅線在報告中是一個引人注目的東西。

回答

6

,首先考慮使用IOUtils.closeQuietly(),這會降低你的未測試的代碼(也可能是重複的)爲:

try { 
     f = new BufferedInputStream(new FileInputStream(source)); 
     f.read(buffer); 
    } finally { 
     IOUtils.closeQuietly(f); 
    } 

現在,它變得堅硬。 「權利」的方式將外部創建BufferedInputStream到另一個類和注入模擬。有一個模擬,你可以驗證是否調用適當的close()方法。

@JeffFoster的答案是非常接近我的意思,但我會建議在組成繼承(在更多的代碼爲代價):

try { 
     f = fileSystem.open(source); 
     f.read(buffer); 
    } finally { 
     IOUtils.closeQuietly(f); 
    } 

哪裏fileSystemFileSystem接口的實例用簡單的真實實現注入生產代碼或模擬測試。

interface FileSystem { 

    InputStream open(String file); 

} 

外部化文件打開的另一個好處是,如果你決定刪除緩衝或添加加密,只需要修改一個地方。

有了這樣的接口實例化與嘲笑(使用的Mockito)測試代碼:

//given 
FileSystem fileSystemMock = mock(FileSystem.class); 
InputStream streamMock = mock(InputStream.class); 

given(fileSystemMock.open("file.txt")).willReturn(streamMock); 

//when 
//your code 

//then 
verify(streamMock).close(); 
+0

我同意。我發現在測試中重寫一個方法非常有用,但它通常是選擇合成方式的中間步驟。在這方面,C#是一個PITA,因爲方法默認情況下不是虛擬的,所以我發現我經常不得不跳過整個過程(這是令人討厭的,因爲您希望儘可能使用最小的更改)。 – 2012-01-10 14:43:55

+0

謝謝,這正是我一直在尋找:)謝謝傑夫 – fyr 2012-01-10 14:47:04

5

你可以重構代碼有點

public class TestMe { 
    public void doSomething() { 
    try { 
     f = new BufferedInputStream(new FileInputStream(source)); 
     f.read(buffer); 
    } finally { 
     if (f != null) 
     try { 
      f.close(); 
     } catch (IOException ignored) { } 
    } 
    } 
} 

要像這樣

public class TestMe { 
    public void doSomething() { 
    try { 
     f = createStream() 
     f.read(buffer); 
    } finally { 
     if (f != null) 
     try { 
      f.close(); 
     } catch (IOException ignored) { } 
    } 
    } 

    public InputStream createStream() { 
     return new BufferedInputStream(new FileInputStream(source)); 
    } 
} 

現在你可以寫你的測試,以捕獲輸入流類,並確認關閉。 (代碼很粗糙,但希望你能得到一般的想法)。

public void TestSomething() { 
    InputStream foo = mock(InputStream.class); // mock object 
    TestMe testMe = new TestMe() { 
    @Override 
    public InputStream createStream() { 
      return foo; 
    } 
    } 

    testMe.something(); 

    verify(foo.close()); 
} 

這是否值得與否是另一回事!

0

你應該注入一個嘲笑BufferedInputStream - 或工廠創建了 - 當模擬的close()方法被調用時再拋一個IOException

此外,我不會說最後的塊,直到你沒有任何邏輯在那裏。

0

我認爲你需要問自己,這是否真的值得測試工作。一些測試愛好者傾向於錯過試圖達到〜100%測試覆蓋率的收益遞減。在這種情況下,它看起來像一些建議的解決方案添加更多複雜性的實際代碼,以使其「可測試」。對於複雜的測試代碼,我很好,但爲了讓它「可測試」而增加實際代碼的複雜性讓我覺得這是一個糟糕的主意。