2013-02-28 97 views
5

我試圖寫一個單元測試此:如何正確模擬自動關閉的資源?

try (final DatagramChannel channel = helper.createChannel()) { 

... 

} 

在我的測試,我嘲笑助手(使用的Mockito),並告訴helper.createChannel()返回一個嘲笑道。

此測試失敗,

java.lang.NullPointerException 
at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:111) 

據我所知,試與 - 資源工廠在Java中在退出try塊調用了DatagramChannel的close()方法,但應該不是關閉()方法在模擬的DatagramChannel被調用?

調試器告訴我AbstractInterruptibleChannel中的closeLock爲null。

我應該繼承DatagramChannel嗎?重寫close()方法,然後模擬我的子類嗎? 或者,我是否以更深刻的方式做了錯誤的事情(幫手模擬模擬)?

問候, 弗雷德裏克Israelsson

測試代碼,根據要求:

@Mock 
private InetAddress peerAddress; 
@Mock 
private UDPChannelHelper helper; 
@Mock 
private DatagramChannel channel; 

private UDPTransportImpl transport; 

@Before 
public void setUp() throws Exception { 
    MockitoAnnotations.initMocks(this); 
    when(helper.createChannel()).thenReturn(channel); 
    transport = new UDPTransportImpl(peerAddress, 0, helper); 
} 

@Test 
public void testNormalSubmit() throws Exception { 
    transport.submit("Hello"); 
} 

正如你所看到的,我不指定任何channel.close行爲()。我相信我不應該,因爲close()返回void。

+0

你能展示你的代碼在哪裏嘲笑這些嗎?也有你通過調試,並確認a)輔助實際上是一個模擬,b)helper.createChannel()也返回一個模擬對象? – cowls 2013-02-28 08:50:44

+0

添加了測試代碼,並且在調試器中,助手和通道的類型都是BlaBlaBla $$ EnhancerByMockitoWithCGLIB。 – 2013-02-28 09:06:55

+1

本頁面:http://mockito.googlecode.com/svn/tags/latest/javadoc/org/mockito/Mockito.html#doNothing%28%29明確指出「虛擬模擬方法默認不做任何事情!」。所以我們只能假設finally塊不是在模擬上執行的。你的代碼對我來說看起來很好,儘管如此我不知道爲什麼這是..你當然不應該需要自己的子類。 – cowls 2013-02-28 09:34:35

回答

6

您正在嘲笑真正的課DatagramChannel,這延伸AbstractInterruptibleChannel。但AbstractInterruptibleChannel.close是最終的,Mockito目前不能嘲笑最終代碼。這就解釋了爲什麼你在代碼中擁有NPE。

我必須提醒你,人們普遍認爲你不擁有的嘲笑類型是不好的做法。我曾經看到過人們這樣做,並且幾年後,當真正的實現發生了變化時,他們有了不好的驚喜,但模擬行爲沒有發生,所以他們錯誤地認爲在更新庫的版本時一切正常。

如果你想繼續這種方式,因爲你有正當的理由(有一些),你可以返回一個接口模擬,如Channel實際上延伸Closeable。或者您可以使用任何其他需要與之交互的界面,這些界面出現在DatagramChannel中。另外如果您需要多個接口,請使用mock(Channel.class, withSetting().extraInterfaces(...))

希望幫助 乾杯, 布萊斯

1

保持拋開你是否應該這樣做或沒有,你可以解決此問題的一個方法是通過「固定」的AbstractInterruptibleChannel模擬實例(無論是FileChannel,一DatagramChannel等),通過爲closeLock字段提供一個Object用於同步關閉呼叫。

private static void fixChannelMock(AbstractInterruptibleChannel mockFileChannel) throws Exception { 
    Field closeLockField = AbstractInterruptibleChannel.class.getDeclaredField("closeLock"); 
    closeLockField.setAccessible(true); 
    closeLockField.set(mockFileChannel, new Object()); 
} 

準備好要修復到小的Java版本上面的代碼中,雖然作爲內部實現AbstractInterruptibleChannel的可能會發生變化。

0

我有同樣的問題,並使用間諜(..)而不是模擬(..)已爲我工作。我試圖在截斷文件時模擬錯誤,以及我的系統是否相應地處理錯誤。

FileChannel fileChannel = spy(FileChannel.class); 
mockStatic(FileChannel.class); 
when(FileChannel.open(eq(filePath), eq(StandardOpenOption.WRITE))).thenReturn(fileChannel); 
when(fileChannel.truncate(1000L)).thenThrow(new IOException("Unable to truncate file")); 

... 

// Snippet being tested! 
fileChannel = FileChannel.open(filePath, StandardOpenOption.WRITE); 
fileChannel.truncate(1000L); // Will throw the exception!