本质上,我有一个Java类,它在套接字通道上执行选择,我想存根通道,这样我就可以按预期测试选择工作。
例如,这大致就是被测试的类所做的:
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更复杂:似乎我需要截取选择器。open()
或以某种方式提供自定义系统默认选择器或提供程序。如果我只是存根SocketChannel,当我试图注册通过选择获得的选择器时,我会得到一个非法的SelectorException。用我的存根打开()
,而baseAbstractSelectableChannel#register
方法不幸是最终的。
但是我找不到任何有用的建议来说明Spock Mocks是如何或是否可能实现这一点的,而且这似乎是一件很常见的事情,所以在这里问一个好问题。有人能帮忙吗?
我想我可能已经找到了我自己问题的答案。
所以选择器。open()。提供者()。openSelector()
,和SocketProvider。provider()
是SocketProvider的惰性静态访问器。提供者
字段。(至少在我的例子中是Java 7)
因此,我们可以简单地设置这个提供者
字段,即使它是私有的,因为Groovy可以忽略正常的Java可见性限制。一旦设置为我们自己的存根实例,以后所有的Selector.open()
调用都将使用它(有一个明显的警告,这是一个全局更改,可能会影响其他未测试的代码)。
细节取决于您当时想要做什么,但如下所示,您可以返回其他类的存根,例如AbstractSelectableChannel。
工作示例如下。
class SocketStubSpec extends Specification {
SocketChannel makeSocketChannel(List events) {
// Insert our stub SelectorProvider which stubs everything else
// required, and records what happened in the events list.
SelectorProvider.provider = Stub(SelectorProvider) {
openSelector() >> {
Map<SelectionKey, AbstractSelectableChannel> keys = [:]
return Stub(AbstractSelector) {
register(_,_,_) >> { AbstractSelectableChannel c, int ops, Object att ->
events << "register($c, $ops, $att)"
SelectionKey key = Stub(SelectionKey) {
readyOps() >> { events << "readyOps()"; ops }
_ >> { throw new Exception() }
}
keys[key] = c
return key
}
select() >> {
events << "select()"
return keys.size()
}
selectedKeys() >> { keys.keySet() }
_ >> { throw new Exception() }
}
}
_ >> { throw new Exception() }
}
return Stub(SocketChannel) {
implConfigureBlocking(_ as Boolean) >> { boolean state -> events << "implConfigureBlocking($state)" }
_ >> { throw new Exception() }
}
}
def "example of SocketChannel stub with Selector" () {
given:
List events = []
// Create a stub socket
SocketChannel channel = makeSocketChannel(events)
Selector selector = Selector.open()
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
expect:
selector.select() == 1 // our implementation doesn't block
List keys = selector.selectedKeys().asList()
keys == [key] // we have the right key
key.isReadable() // key is readable, etc.
// Things happened in the right order
events == [
"implConfigureBlocking(false)",
"register(Mock for type 'SocketChannel', 1, null)",
"select()",
"readyOps()",
]
}
}
Spock使用CGLIB模拟/存根/间谍类。CGLIB不能覆盖final方法。SocketCannel有很多最终方法(例如configureBlocking),但是CGLIB不会失败,而是使用原始方法。由于configureBlocking是最终版本,因此将在测试中使用它。
public final SelectableChannel configureBlocking(布尔块)抛出IOException{synchronized(regLock){if(!isOpen())抛出新的ClosedChannelException();if(blocking==block)返回此值;if(block)
所以configureBlocking需要初始化regLock变量,但是当你为这个类创建存根时,这个变量没有初始化,你在这里得到了NPE。
问题是如何处理它?嗯,我会说,首先,试着使用接口,而不是类。如果不可能,试着不要调用最终方法。如果仍然不可能,你必须看看类的内部,找出什么应该被嘲笑。我看到的最后一个选择是做一个完全集成的测试:创建两个套接字并将它们连接起来。
我在micronaut中有以下接口来执行HTTP POST请求: 我有一个调用接口的类: 我想在我的spock测试中模拟/存根API调用,我尝试了以下方法: 然而,我得到的错误:
问题内容: 那是测试的方法 我必须模拟三个CacheContext,Method和CacheEnable。有什么想法可以使测试用例更加简单吗? 问题答案: Mockito 可以处理链接的存根: AFAIK,链中的第一个方法返回一个模拟,该模拟被设置为在第二个链式方法调用中返回您的值。 Mockito的作者注意到,这 仅应用于遗留代码 。否则,更好的方法是将行为推送到CacheContext并提供完
问题内容: 拿这个对象: 如果我这样做: 然后y将返回。通过stringify传递函数有什么能做的吗?使用“ ye goode olde eval()”可以创建具有附加功能的对象,但是打包该对象又是什么呢? 问题答案: 您不能打包函数,因为任何序列化程序都看不到函数关闭的数据。甚至Mozilla 也无法正确打包闭包。 最好的选择是使用复活器和替换器。 https://yuilibrary.com/
我有一个复杂的变量在我的自定义块模板中。是当前网站的语言,但内容仅以当时的语言交付,当缓存建造。 我的渲染数组中确实有语言,它适用于twig模板中的命令: 有没有办法让Drupal根据页面的当前语言处理多个缓存条目? 太多了<安德烈亚斯
虽然这个问题已经得到了回答,但我仍然不清楚在嘲弄中应该使用哪一个 当参考。我看不出它们之间有什么区别。 留档为是说 的文档中说 的留档是说 这清楚地表明,这两者之间没有区别。那么,我们为什么要采用这三种嘲弄策略,以及在当时和何时使用它们之间的具体区别。 如果它是一个带有示例代码的答案,那将非常有帮助。
问题内容: 我需要运行python脚本,并确保它在终止后将重新启动。我知道有一个称为supervisor的UNIX解决方案。但是不幸的是,必须运行我的脚本的服务器在Windows上。您知道哪种工具有用吗?谢谢 问题答案: 尽管这里有大量的免责声明,但是您可以在Windows中使用Cygwin运行Supervisor 。事实证明,Cygwin在模拟Posix环境方面走了很长的路要走,以至于实际上Su