考虑以下示例
@AllArgsConstructor
class Foo{
private AHandler ahandler;
private BHandler bhandler;
public void handle(String what){
if(what.equals("A")){
ahandler.handle(what);
}else if(what.equals("B"){
bhandler.handle(what)
}else throw new RuntimeException();
}
}
现在我尝试测试if逻辑,其中必须调用给定的输入适当的处理程序,但我不知道如何动态指定Assert。以下不起作用(但我认为显示了意图)
def ahandler=Mock(AHandler);
def bhandler=Mock(BHandler);
def service=new Foo(ahandler,bhandler);
when:
service.handle(input);
then:
1 * expectedCalls
0 * _ //no other calls
where:
input | expectedCalls
"A" | ahandler.handle("A")
"B" | bhandler.handle("B")
有没有可能和斯波克做这种事?
2条答案
按热度按时间pbpqsu0x1#
像往常一样,经过一些尝试和错误(谷歌和检查github spock问题),我找到了令人满意的解决方案。
8ljdwjyq2#
虽然您自己的解决方案在修复了错误和不完整的示例代码后仍然有效,但我认为这种过度设计的解决方案并不好。
Antonio解决方案
首先,您的原始代码假设您有方法
AHandler.handle(String)
,但BHandler.handler(String)
在方法名称的末尾带有“r”。斯波克规格假设相同。然后,在你自己的答案中,你切换到了两个处理程序类的统一handle(String)
方法,这可能更有意义,但考虑到你的问题,这样解决是不可能的。在
this."$handler".handle(_)
中需要显式的前导this.
,这也是非常丑陋的。一旦您将mock定义从feature方法中的字段移动到局部变量,这种解决方案也会崩溃。因此,解决方案是脆弱的。无论如何,下面是一个MCVE,它再现了您的问题和解决方案:更优雅的解决方案
相反,你可以这样做,甚至使用局部变量进行模拟:
或者,假设两个处理程序类都实现了一个公共接口,我们可以设计一个更类型安全的解决方案:
现在,我的IDE自动将
def handlers
解释为Map<String, MyHandler> handlers
,并且.handle(input)
调用的未知调用目标的警告消失了。当然,如果
Foo
中的字段定义的类型是MyHandler
而不是AHandler
和BHandler
,那么可以从Spock规范中删除更多的警告,但这超出了范围,也许你需要确切的类型。我知道你的代码只是示例代码。我只是想指出解决这个问题的方法,而不需要解析字符串来解析方法名。在Groovy Web Console中尝试。
我的解决方案的外观变化
**更新:**我重新访问了这段代码,因为我正在处理另一个问题,想知道我们是否可以不使用map,也不需要以DRY的方式在所有地方重复
'A'
和'B'
。这是其中之一:有些更深奥,避免
index
数据变量,并利用Spock 2.0iterationIndex
。我并不推荐这个变体,只是为了完整起见而提到它:请注意,这两种解决方案都使用spread操作符来调用构造函数
new Foo(*handlers)
。现在我们有了一个有序的mock列表而不是一个无序的map,使用spread操作符是安全的。