首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何正确模拟自动关闭的资源?

如何正确模拟自动关闭的资源?
EN

Stack Overflow用户
提问于 2013-02-28 16:46:59
回答 3查看 3.8K关注 0票数 6

我正在尝试为此编写一个单元测试:

代码语言:javascript
复制
try (final DatagramChannel channel = helper.createChannel()) {

...

}

在我的测试中,我模拟了助手(使用Mockito),并告诉helper.createChannel()返回一个模拟的通道。

此测试失败,出现以下错误

代码语言:javascript
复制
java.lang.NullPointerException
at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:111)

我知道Java中的try-with-resources工具在退出try块时调用DatagramChannel中的close()方法,但是不应该调用模拟的DatagramChannel中的close()方法吗?

调试器告诉我AbstractInterruptibleChannel中的closeLock为空。

我应该继承DatagramChannel的子类,覆盖其中的close()方法,然后模拟我的子类吗?或者,我以更深刻的方式做错了什么( helper mock返回一个mock)?

问候,弗雷德里克·伊斯雷森

测试代码,根据请求:

代码语言:javascript
复制
@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。

EN

回答 3

Stack Overflow用户

发布于 2013-02-28 21:36:25

您正在模拟一个扩展AbstractInterruptibleChannel的真实类DatagramChannel。然而,AbstractInterruptibleChannel.close是最终的,而Mockito目前不能模拟最终的代码。这就解释了为什么你在代码中有一个NPE。

我必须提醒您,人们普遍认为,模仿您不拥有的类型是一种糟糕的做法。我见过人们这样做,几年后,当真正的实现发生变化时,他们会有糟糕的惊喜,但模拟行为并没有,所以当他们更新库的版本时,他们错误地认为一切都是正常的。

但是,如果您想继续这种方式,因为您有充分的理由(确实有一些),您可以返回一个模拟的接口,比如实际上扩展了CloseableChannel。或者,您可以使用DatagramChannel中提供的任何其他需要与之交互的接口。另外,如果需要多个接口,只需使用mock(Channel.class, withSetting().extraInterfaces(...))即可。

希望这能帮你干杯,Brice

票数 8
EN

Stack Overflow用户

发布于 2017-11-22 06:00:08

我也遇到了同样的问题,使用spy(..)代替mock(..)对我很管用。我试图在截断文件时模拟一个错误,我的系统正在相应地处理该错误。

代码语言:javascript
复制
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!
票数 2
EN

Stack Overflow用户

发布于 2015-02-10 13:19:00

抛开您是否应该这样做不谈,解决此问题的一种方法是“修复”AbstractInterruptibleChannel模拟实例(无论是FileChannel、DatagramChannel等)。通过为用于同步close调用的closeLock字段提供对象。

代码语言:javascript
复制
private static void fixChannelMock(AbstractInterruptibleChannel mockFileChannel) throws Exception {
    Field closeLockField = AbstractInterruptibleChannel.class.getDeclaredField("closeLock");
    closeLockField.setAccessible(true);
    closeLockField.set(mockFileChannel, new Object());
}

尽管AbstractInterruptibleChannel的内部实现可能会发生变化,但要准备好在Java小版本上修复上面的代码。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15131105

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档