简体   繁体   English

如何在spock / groovy中监视闭包或代理?

[英]How to Spy on a Closure or Proxy in spock/groovy?

How to Spy on a Closure or Proxy in spock/groovy? 如何在spock / groovy中监视闭包或代理?

Spock has a limitation that you can't use the following style interaction testing pattern: Spock有一个限制,即您不能使用以下样式交互测试模式:

setup:
subscriber.receive("message1") >> "ok"

when:
publisher.send("message1")

then:
1 * subscriber.receive("message1")

Because the 1 * interaction line overrides the stubbed return of the "ok". 因为1 *交互行会覆盖“ ok”的存根返回。 You have to instead put them both on the same line: 您必须将它们放在同一行:

1 * subscriber.receive("message1") >> "ok"

Mostly this is ok, but for some cases with the need to stub just a bit more complicated behaviour it'd be really nice to be able to separate stubbed behaviour and interaction like that. 通常这是可以的,但是在某些情况下,仅需要稍微复杂一些的行为,就能够将这样的行为和交互分开是非常好的。

All the things I'm mocking/stubbing can be represented by a Closure , but I can't get a Spy to a Closure to work (or a Spy to a Proxy to a Closure ). 所有我嘲笑的东西/存根可以由代表Closure ,但我不能让一个Spy到一个Closure的工作(或SpyProxyClosure )。 So I have the following Closure-lite class: 所以我有以下Closure-lite类:

#!groovy
import spock.lang.Specification

class C {
    def c
    C (Closure c) { this.c = c }

    def call (Map args = null) { args? c?.call (args) : c?.call() }
    def call (Map args = null, one) { args? c?.call (args, one) : c?.call (one) }
    def call (Map args = null, one, two) { args? c?.call (args, one, two) : c?.call (one, two) }
    // etc.
}

That lets the following tests pass despite their separation of stubbed behaviour and interaction: 尽管分离了行为和交互,但以下测试仍然可以通过:

class spyTest extends Specification {

    void "should accept 0 args" () {
        given:
            def foo = Spy (C, constructorArgs:[{return "foo"}])
        when:
            def result = foo ()
        then:
            1 * foo()
            result == "foo"
    }

    void "should accept 1 positional args, 0 named args" () {
        given:
            def foo = Spy (C, constructorArgs:[{ one -> ["foo", one]}])
        when:
            def result = foo (1)
        then:
            1 * foo(1)
            result == ["foo", 1]
    }

    void "should accept 1 args, + 1 named args" () {
        given:
            def foo = Spy (C , constructorArgs:[{ args, one -> return ["foo", args, one]}])
        when:
            def result = foo (1, a:'a')
        then:
            1 * foo(1, a:'a')
            result == ["foo", [a:'a'], 1]
    }

    void "should accept 2 args, + 0 named args" () {
        given:
            def foo = Spy (C , constructorArgs:[{ one, two -> return ["foo", one, two]}])
        when:
            def result = foo (1,2)
        then:
            1 * foo(1,2)
            result == ["foo", 1, 2]
    }

    void "should accept 2 args, + 2 named args" () {
        given:
            def foo = Spy (C , constructorArgs:[{ args, one, two -> return ["foo", args, one, two]}])
        when:
            def result = foo (1,2, a:'a', b:'b')
        then:
            1 * foo(1,2,a:'a', b:'b')
            result == ["foo", [a:'a', b:'b'], 1, 2]
    }

}

This is workable, if a little clunky, but obviously it'd be nicer to have a more dynamic "Closure" class than one where I have to add every potential number of parameters. 如果有点笨拙,这是可行的,但是显然,拥有一个动态的“关闭”类比在其中我必须添加所有可能数量的参数的类更好。

So is a more dynamic solution possible (how?), or does the fact of using Spy or GroovySpy prohibit a more dynamic spied-upon class? 那么有可能使用更动态的解决方案(如何?),或者使用SpyGroovySpy的事实GroovySpy禁止了更动态的GroovySpy类?

Could you elaborate a bit more, everything you have shown can be easily done with mocks no need for a spy. 您能否详细说明一下,您可以通过模拟轻松完成显示的所有内容,而无需间谍。


class MockTest extends Specification {

  void "should accept 0 args" () {
    given:
    def foo = Mock (C)
    when:
    def result = foo ()
    then:
    1 * foo() >> 'foo'
    result == "foo"
  }

  void "should accept 1 positional args, 0 named args" () {
    given:
    def foo = Mock (C)
    when:
    def result = foo (1)
    then:
    1 * foo(1) >> { args -> ["foo", args[0]]}
    result == ["foo", 1]
  }

  void "should accept 1 args, + 1 named args" () {
    given:
    def foo = Mock (C)
    when:
    def result = foo (1, a:'a')
    then:
    1 * foo(1, a:'a') >> { args, one -> ["foo", args, one]}
    result == ["foo", [a:'a'], 1]
  }

  void "should accept 2 args, + 0 named args" () {
    given:
    def foo =  Mock (C)
    when:
    def result = foo (1,2)
    then:
    1 * foo(1,2) >> { one, two -> ["foo", one, two]}
    result == ["foo", 1, 2]
  }

  void "should accept 2 args, + 2 named args" () {
    given:
    def foo = Mock (C)
    when:
    def result = foo (1,2, a:'a', b:'b')
    then:
    1 * foo(1,2,a:'a', b:'b') >> { args, one, two -> ["foo", args, one, two]}
    result == ["foo", [a:'a', b:'b'], 1, 2]
  }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM