当前位置: 首页 > 知识库问答 >
问题:

用Spock模拟一个“阻塞”方法调用?

常鸿朗
2023-03-14

我正在学习使用Spock进行单元测试,遇到了一个我似乎无法理解的问题:

注意:这个例子非常简化,但它得到了我想要实现的概念。

while(listening) {
    try {
        Socket socket = serverSocket.accept();
        doSomethingWithSocket(socket);
    } catch(IOException ex) {
        ex.printStackTrace();
        listening = false;
    }
}

在正常操作中,ServerSocket.Accept()调用会阻塞,直到建立到ServerSocket的连接。

我希望能够测试ServerSocket.Accept()返回的Socket上的交互。我可以通过以下方式与Spock一起做到这一点:

given: "A ServerSocket, Socket, and Listener"
    def serverSocket = Mock(ServerSocket)
    def socket = Mock(Socket)
    serverSocket.accept() >> socket
    def listener = new Listener(serverSocket)

when: "Listener begins listening"
    listener.startListening()

then: "Something should be done with the socket"
    // Verify some behavior on socket

乍一看,这工作得很好,只是对ServerSocket.Accept()的每次调用都将返回经过嘲弄的Socket。由于此调用(有意地)被调用的次数不确定(因为我想接受不确定数量的入站连接),所以mocksocket上的所有交互发生的次数不确定(取决于计算机的速度、运行时间等)

我可以使用交互的基数来指定至少一个交互,如下所示:

1.._ * socket.someMethod()

但这件事让我感觉不对劲;我不是真的在寻找至少一个交互,我是真的在寻找一个交互。

我可以这样做(返回一次模拟的套接字然后为空):

serverSocket.accept() >>> [socket, null]

但我仍然有大量对DoSomethingWithSocket的调用,这些调用传递一个null参数,然后我必须检查并忽略(或报告)该参数。如果忽略它,我可能会错过报告合法的问题(我不认为serversocket#accept会返回null,但由于类不是最终的,可能有人实现了他们自己的版本,可以这样做),但是如果我报告它,我的测试日志会受到日志消息的污染,该消息将预期的结果报告为意外的结果。

诚然,我不是一个Groovy程序员,而且这是我第一次和Spock一起工作,所以我很抱歉,如果这是一个简单的错误的事情,或者如果我误解了Spock是如何嘲弄的

我试过这个:

serverSocket.accept() >> socket >> {while(true) {}; null }

在调用accept方法之前,此循环将永远循环;我不是100%确定为什么,因为我认为直到第二次调用accept方法时才会对闭包进行评估?

我也试过这个:

serverSocket.accept() >>> [socket, { while(true){}; null }]

我试图验证的不是阻塞本身;我想验证每个特征方法的一个行为。我可以使用下面David W提供的技术在一个方法中测试许多行为,但是有些行为会根据接受的连接数量、是否遇到了异常、侦听器是否被告知停止接受连接等等而改变。这将使单一特性方法变得更加复杂,并且难以进行故障排除和文档化。

如果我可以从accept方法返回定义数量的socket模拟,然后将该方法设置为块,我就可以单独和确定性地验证所有这些行为,每个feature方法一个。

共有1个答案

能翔宇
2023-03-14

单元测试应该只测试一个类,而不是对其他类的依赖。从您粘贴的代码来看,您只需要验证两件事

  1. 该类使用提供的套接字重复调用doSomethingWithSocket
  2. 它在引发IO异常后停止

假设doSomething只是传递给一个委托类DoSomethingDelegate

setup:
def delegate = Mock(DoSomethingDelegate)
List actualExceptions = [null,null,new IOException()]
int index = 0
// other setup as in your question

when:
new Listener(serverSocket).startListening()

then:
noExceptionThrown()
3 * delegate.doSomethingWithSocket(socket) {
  if(actualExceptions[index]) {
    throw actualExceptions[index]
  }
  index++
}
setup:
List sockets = []
sockets << Mock(Socket)
sockets << Mock(Socket)
// repeat as needed
socket[3].isClosed() >> {Thread.sleep(1000);false}
serverSocket.accept() >> {sockets[socketNumber]}
// add different mock behavior
// when block
// then
where:
socketNumber << [1,2,3]

如果您需要一个最后的套接字来容纳主线程,您可以像我上面所做的那样使它Hibernate。您可以通过使方法调用彼此交互来添加更复杂的行为。

 类似资料:
  • 注意:这个示例非常简单,但它得到了我想要实现的跨越的想法。 我有一个类(称为),它接受作为构造函数参数;它有一个方法,该方法生成一个执行以下操作的新线程(为简洁起见,大大减少了): 在正常操作中,调用会阻塞,直到与建立连接为止。 但这让我产生了错误的感觉;我不是真的在寻找至少一个互动,我真的在寻找一个互动。 我可以这样做(返回一次模拟的套接字,然后返回null): 但是,我仍然有大量对的调用,这些

  • 1)创建groovy项目 2)创建接口: 3)创建spock测试: 有没有更好的方法来“解释”spock被模仿函数的最后一个参数是vararg,因此可以省略它?

  • 我目前正在为一个groovy应用程序编写单元testcase 有人能告诉我这是不是嘲弄斯波克的两个电话的正确方法?如果没有,那么请引导我走向正确的解决方案。

  • 我试图为一个类编写一个单元测试,这个类使用带有库中的的Google vision API。问题是,由于某种原因,我的模拟仍然调用真正的方法,然后抛出一个NPE,这破坏了我的测试。我以前从未在模拟上见过这种行为,我想知道我是不是做错了什么,是不是Spock/Groovy中有bug,还是与Google lib有关?

  • 我正在处理一个Spring Boot java服务,该服务包含一个Camel处理器类,如下所示: 当我运行它时,我得到一个失败,声明对1*LogService.Update(_)的调用太少(0个调用)。我试着调试代码,在MyProc中,语句被命中,并且logService对象在高亮显示时(在Eclipse中)状态为“mock for type logService named$spock_shar

  • 我试图在测试中模拟一个调用,但我得到了一个错误,因为它调用了真正的方法,而不是模拟它。 这是我的方法 } 这是我的测试课 测试实际上调用了受保护的方法config Setter,并在设置代理时失败。帮助我理解我在这里做错了什么。