基本上我有一个Java类,它在套接字通道上执行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,这样我就不必创建一个真正的客户端进行测试。
我也知道它比仅仅存根SocketChannel更复杂:似乎我需要拦截Selector.open()
或以某种方式提供自定义系统默认的SelectorProvider。如果我只是存根SocketChannel,当我尝试通过Selection.open()
注册通过我的存根获取的选择器时,我得到一个IllegalSelectorException,并且基础AbstractSelectableChannel#register
方法很遗憾是最终的。
但是我找不到任何关于如何或是否可以使用Spock Mocks的有用指针,而且看起来它可能是非常普遍的想法,所以这里要问一个很好的问题。有人可以帮忙吗?
答案 0 :(得分:1)
Spock使用CGLIB来模拟/存根/间谍类。 CGLIB无法覆盖最终方法。 SocketChannel有很多最终方法(例如configureBlocking),但CGLIB不会失败但使用原始方法。由于configureBlocking是final,因此在测试中使用它。
public final SelectableChannel configureBlocking(boolean block)
throws IOException
{
synchronized (regLock) {
if (!isOpen())
throw new ClosedChannelException();
if (blocking == block)
return this;
if (block && haveValidKeys())
throw new IllegalBlockingModeException();
implConfigureBlocking(block);
blocking = block;
}
return this;
}
所以configureBlocking需要初始化regLock变量,但是当你为这个类创建存根时,变量没有初始化,你在这里获得了NPE。
问题是如何处理它? 好吧,我要说,首先,尝试使用接口而不是类。 如果不可能,请尽量不要调用最终方法。 如果它仍然不可能你必须查看课堂内部并找出应该嘲笑的内容。 我看到的最后一个选项是进行完整的集成测试:创建两个套接字并连接它们。
答案 1 :(得分:0)
我想我可能已经找到了自己问题的答案。
因此?FuelEconomy
无法直接拦截 - 但它只是调用data(FuelEconomy)
dim(cars2010)
[1] 1107 14
,而Selector.open()
是SocketProvider.provider().openSelector()
字段的惰性静态访问器。 (至少在我的例子中,Java 7)
因此,我们可以简单地设置此SocketProvider.provider()
字段,即使它是私有的,因为Groovy可以忽略正常的Java可见性限制。一旦设置为我们自己的存根实例,所有未来的SocketProvider.provider
调用将在此后使用它(明显需要注意这是一个全局更改,可能会影响其他未测试的代码)。
详细信息取决于您当时要执行的操作,但如下所示,您可以返回其他类的存根(例如AbstractSelectableChannel)。
工作示例如下。
provider