我正在尝试为此编写单元测试:
try (final DatagramChannel channel = helper.createChannel()) {
...
}
在我的测试中,我模拟了帮助程序(使用Mockito),并告诉helper.createChannel()返回一个模拟的通道。
此测试失败,
java.lang.NullPointerException
at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:111)
据我所知,Java中的try-with-resources工具在退出try块时调用DatagramChannel中的close()方法,但是不应该调用模拟的DatagramChannel中的close()方法吗?
调试器告诉我AbstractInterruptibleChannel中的closeLock为null。
我应该继承DatagramChannel,重写其中的close()方法,然后模拟我的子类吗? 或者,我是否以更深刻的方式做错了(帮助器模拟返回模拟)?
此致 Fredrik 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 :(得分:7)
你正在嘲笑一个真正的班级DatagramChannel
,它扩展了AbstractInterruptibleChannel
。但是AbstractInterruptibleChannel.close
是最终的,Mockito目前无法模拟最终代码。这解释了为什么代码中有NPE。
我必须提醒你,人们普遍认为你不拥有的嘲弄类型是不好的做法。我已经看到人们这样做了,多年后当真正的实现发生了变化时他们就有了不好的惊喜,但模拟行为却没有,所以当他们更新库的版本时,他们错误地认为一切都没问题。
如果你想继续这种方式,因为你有充分的理由(并且有一些),你可以返回一个界面的模拟,比如实际扩展Channel
的{{1}}。或者,您可以使用Closeable
中存在的与您需要进行交互的任何其他界面。此外,如果您需要多个界面,请使用DatagramChannel
。
希望有所帮助 干杯, 布莱斯
答案 1 :(得分:2)
我有同样的问题,使用间谍(..)而不是mock(..)对我有用。我试图在截断文件时模拟错误,而我的系统正在相应地处理错误。
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 :(得分:1)
不管你是否应该这样做,你可以解决这个问题的一种方法是通过为{{提供一个Object'来“修复”AbstractInterruptibleChannel模拟实例(无论是FileChannel,DatagramChannel等)。 1}}用于同步近距离呼叫的字段。
closeLock
准备好必须在次要Java版本上修复上述代码,因为AbstractInterruptibleChannel的内部实现可能会发生变化。