2013-02-28 53 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!