如何使用Spock框架进行参数捕获?

43

我有一些类似这样的Java代码:

public interface EventBus{
    void fireEvent(GwtEvent<?> event);
}


public class SaveCommentEvent extends GwtEvent<?>{
    private finalComment oldComment;
    private final Comment newComment;

    public SaveCommentEvent(Comment oldComment,Comment newComment){
        this.oldComment=oldComment;
        this.newComment=newComment;
    }

    public Comment getOldComment(){...}
    public Comment getNewComment(){...}
}

并测试如下代码:

  def "...."(){
     EventBus eventBus=Mock()
     Comment oldComment=Mock()
     Comment newCommnet=Mock()

     when:
         eventBus.fireEvent(new SaveCommentEvent(oldComment,newComment))

     then:
         1*eventBus.fireEvent(
                                {
                                   it.source.getClass()==SaveCommentEvent;
                                   it.oldComment==oldComment;
                                   it.newComment==newComment
                                 }
                              )            
}

我希望验证eventBus.fireEvent(..)被调用一次,触发的事件类型为SaveCommentEvent,并且构造参数包括oldCommentnewComment

代码可以正常运行,但问题是:

在更改了闭包内容之后

{
   it.source.getClass()==SaveCommentEvent;
   it.oldComment==oldComment;  //old==old
   it.newComment==newComment   //new==new
}

To

:这里缺少具体的上下文信息,无法准确翻译。
 {
    it.source.getClass()==Other_Class_Literal;
    it.oldComment==newComment;  //old==new
    it.newComment==oldComment   //new==old
  }

然而,代码并没有出错?显然闭包没有完成我想要的功能,所以问题是:如何进行参数捕获?

6个回答

65

我明白了:

    SaveCommentEvent firedEvent

    given:
     ...

    when:
     ....

    then:
    1 * eventBus.fireEvent(_) >> {arguments -> firedEvent=arguments[0]}
    firedEvent instanceof SaveModelEvent
    firedEvent.newModel == newModel
    firedEvent.oldModel == oldModel

1
或者在原始代码中将;替换为&&。(代码参数约束需要根据它们是否匹配返回truefalse。) - Peter Niederwieser
我发现无论变量定义在哪里,都无法在闭包外部访问捕获的参数。 - orbfish
使用这种方法而不仅仅是将原始代码中的;替换为&&的主要优点是,您可以获得更精细调整的错误消息 - 您知道哪个条件失败了。 - Amit Goldstein
2
Spock似乎将捕获的参数混淆为模拟返回值。其他人是怎么做的?@AlexLuya - Vishal
@Vishal,闭包将返回最后一行作为模拟的返回值,所以如果返回类型与参数类型不同,则应该从闭包中显式地返回正确类型的对象。 - Patrick
如果您想要正确输入参数,那么这个答案会更好:https://dev59.com/_WEh5IYBdhLWcg3wtFT4#67916541 - stwr667

21
then:
     1*eventBus.fireEvent(
                            {
                               it.source.getClass()==SaveCommentEvent;
                               it.oldComment==oldComment;
                               it.newComment==newComment
                             }
                          )            

在你的代码中,it是指一个模拟事件总线接口的Groovy闭包隐式变量引用,该接口没有字段。你如何验证它们?
此外,我认为使用Spock Mocks所必须发生的事件顺序并不一定直观。我可以在这里撰写它,但它不会像Kenneth Kousen的解释那样好。

5
如果我没记错的话,你提供的代码并不是完全正确的。具体来说,只有在 it.newComment==newComment 检查未通过时,测试才会失败。前面两个检查对测试通过或失败都没有影响。这就是 @PeterNiederwieser 在他的评论中暗示的意思。 - geoand
1
这个答案是错误的,正如@geoand所说。前两行被执行了,但它们的结果被忽略了。要么像这样使用:然后: 1*eventBus.fireEvent( { it.source.getClass()==SaveCommentEvent && it.oldComment==oldComment && it.newComment==newComment } ) 或者尝试其他方法。 - nluk

12
在2021年(7年之后),使用Groovy(2.5)可以实现以下功能:
    ...

    then:
    1 * eventBus.fireEvent(_) >> { SaveModelEvent event ->
        assert event.newModel == newModel
        assert event.oldModel == oldModel
    }
    0 * _

对我来说更方便,而且可以节省一两行代码。


唯一的注意事项是它不允许在给定的代码块中声明模拟行为并在其中进行验证。如果模拟不是在单独的方法中进行的,以提高测试的可读性,这将不会成为问题。 - gaal
它还可以用于捕获多个参数,1 * subject.dependencyClass.doSomething(_, _) >> { String a, String b -> a == b} - prayagupa

11

@Alex Luya相同的想法,但将断言放在闭包中并对每个断言使用assert。参见Spock Framework参考文档

    then:
    1 * eventBus.fireEvent(_) >> {
        def firedEvent = it[0]
        assert firedEvent instanceof SaveModelEvent
        assert firedEvent.newModel == newModel
        assert firedEvent.oldModel == oldModel
    }

感谢 @mikerodent,我的意图是链接到多个 assertions 的示例;因此在这方面,该链接看起来是正确的。至于 jeremyjjbrown 的回答,我不确定……@geoand 指出并非所有断言都起作用。我现在没有片段可以进行实验 :p - ryu1kn
啊,"Not very helpful. Fortunately, we can do better:" 下面的那个东西...我没注意到。另外,是的,@geoand 的观点是正确的。我的错误。 - mike rodent

4
如果您想嘲笑一个方法的响应并验证相同方法的参数(与捕获参数相同),则可以使用Spock的代码约束(除其他约束条件外)部分匹配参数,并同时验证方法参数。
1 * list.add({
  verifyAll(it, Person) {
    firstname == 'William'
    lastname == 'Kirk'
    age == 45
  }
}) >> mockedResponse

PS:解决方案的灵感来自@Leonard Brünings的这个回答

1
在@alex-luya的答案中,我发现变量firedEvent需要@Shared注释。然后我可以捕获该值并在闭包之外运行我的检查。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接