java可以用Spock存根或模拟SocketChannel吗?
本质上,我有一个Java类,它在socket通道上执行select,我想存根该通道,以便测试select是否按预期工作
例如,这大致就是被测试的类所做的:
class TestedClass {
TestedClass(SocketChannel socket) { this.socket = socket }
// ...
SocketChannel socket;
// ...
void foo() {
// Wait a while for far end to close as it will report an error
// if we do it. But don't wait forever!
// A -1 read means the socket was closed by the other end.
// If we select on a read, we can check that, or time out
// after a reasonable delay.
Selector selector = Selector.open();
socket.configureBlocking(false);
socket.register(selector, SelectionKey.OP_READ);
while(selector.select(1000) == 0) {
Log.debug("waiting for far end to close socket...")
}
ByteBuffer buffer = ByteBuffer.allocate(1);
if (socket.read(buffer) >= 0) {
Log.debug("far end didn't close");
// The far end didn't close the connection, as we hoped
abort(AbortCause.ServerClosed);
}
Log.debug("far end closed");
}
}
我希望能够测试这样的东西:
def "test we don't shut prematurely" () {
when:
boolean wasClosedPrematurely
SocketChannel socket = Stub(@SocketChannel) {
// insert stub methods here ....
}
TestedClass tc = new TestedClass(socket)
tc.foo();
then:
wasClosedPrematurely == false
}
这是基于一个真实的例子,但细节并不重要。总体目标是如何存根支持选择的SocketChannel,这样我就不必创建真正的客户机进行测试
我也知道这比简单的stub SocketChannel更复杂:似乎我需要拦截Selector.open()
或者以某种方式提供一个自定义的系统默认选择器提供程序。如果我只是stub SocketChannel,当我试图将通过Selection.open()
获得的选择器注册到我的stub时,我会得到一个非法的SelectorException,不幸的是,base AbstractSelectableChannel#register
方法是最终的
但我找不到任何有用的指示,来说明这在Spock Mocks中是如何或是否可能实现的,而且这似乎是一件很常见的事情,所以在这里问一个好问题。有人能帮忙吗
# 1 楼答案
我想我可能已经找到了我自己问题的答案
所以
Selector.open()
不能直接被拦截,但它只是调用SocketProvider.provider().openSelector()
,而SocketProvider.provider()
是SocketProvider.provider
字段的惰性静态访问器。(至少在我的例子中是Java 7)因此,我们可以简单地设置这个
provider
字段,即使它是私有的,因为Groovy可以忽略正常的Java可见性限制。一旦设置为我们自己的存根实例,以后所有的Selector.open()
调用都将使用它(需要注意的是,这是一个全局更改,可能会影响其他未测试的代码)细节取决于您当时想要做什么,但如下所示,您可以返回其他类的存根,例如AbstractSelectableChannel
工作示例如下