Unit testing Groovy/Spock:指定将哪些值传递到模拟的闭包中

Unit testing Groovy/Spock:指定将哪些值传递到模拟的闭包中,unit-testing,groovy,mocking,spock,Unit Testing,Groovy,Mocking,Spock,首先,这是我和斯波克的第一天,所以请对我有耐心;- 我如何为下面的测试课程设定期望值 class JobThing { Logger log; void loppy(Sql sql) { sql.eachRow("select x from y") { z -> log.info("${z.x}") } } } 我想假设select语句返回a,然后返回b。 这就是我想到的 def "loppy"() {

首先,这是我和斯波克的第一天,所以请对我有耐心;-

我如何为下面的测试课程设定期望值

class JobThing {
    Logger log;

    void loppy(Sql sql) {
        sql.eachRow("select x from y") {
            z -> log.info("${z.x}")
        }
    }
}
我想假设select语句返回a,然后返回b。 这就是我想到的

def "loppy"() {
    setup:
    def job = new JobThing()
    job.log = Mock(Logger)
    Sql sql = Mock(Sql)

    when:
    job.loppy(sql)

    then:
    //if sql.eachRow(...) is called then call its closure with value "a" then value "b"
    1 * sql.eachRow('select x from y') {
        // declare expected values here? or where?
    }
    1 * job.log.info("a")
    1 * job.log.info("b")
}
这只是我想到的众多变体之一。。。到目前为止,没有一个能起作用。因为我似乎找不到好的关键字来搜索,所以文档并没有多大帮助


谢谢

首先,你需要考虑你要测试什么。洛皮似乎不够明确。 我不确定,所以粗略地说,将您的测试分为两种特性方法,如下所示:

class JobThingSpec extends Specification {

    def sql = Mock(Sql)
    def log = Mock(Logger)
    def job = new JobThing(log: log)

    def 'proper query is executed'() {
        when:
        job.loppy(sql)

        then:
        1 * sql.eachRow('select x from y', _)

    }

    def 'query result is logged'() {
        given:
        sql.rows(_) >> [[x:'a'], [x:'b']]

        when:
        job.loppy(sql)

        then:
        1 * log.info('a')
        1 * log.info('b')
    }
}
void loppy(Sql sql) {
    List<Map> result = sql.rows("select x from y")
    result.each { z ->
        log.info("${z.x}")
    }
}
通过这种方式,您可以将您的案例很好地分解为单独的测试步骤——这本身就具有更好的可读性、测试失败反馈

此外,您不必担心通过的闭包,因为它不容易测试,而且在您的情况下是毫无意义的。但如果你还需要,请告诉我,我们可以找到一个办法

更新:

只需如下调整您的方法:

class JobThingSpec extends Specification {

    def sql = Mock(Sql)
    def log = Mock(Logger)
    def job = new JobThing(log: log)

    def 'proper query is executed'() {
        when:
        job.loppy(sql)

        then:
        1 * sql.eachRow('select x from y', _)

    }

    def 'query result is logged'() {
        given:
        sql.rows(_) >> [[x:'a'], [x:'b']]

        when:
        job.loppy(sql)

        then:
        1 * log.info('a')
        1 * log.info('b')
    }
}
void loppy(Sql sql) {
    List<Map> result = sql.rows("select x from y")
    result.each { z ->
        log.info("${z.x}")
    }
}

您对sql对象进行了模拟,但尚未为每个方法定义行为。别指望那个傻瓜自己知道该做什么。但是,即使您定义了mock方法,您也应该测试mock而不是实现,这是毫无意义的。您可以尝试使用Spy而不是Mock,但目前您的实现没有“tes友好”设计。我如何定义Sql.eachRow的行为?我发现很多例子告诉我如何定义返回值,但是如何定义在第一次、第二次、第三次调用时传递到闭包中的内容呢?您能提供所需的代码吗?谢谢!您的第二个方法失败,因为sql.eachRowString,Closure返回void无法将类为“java.util.ArrayList”的对象“[{x=a},{x=b}]”强制转换为类“void”,原因是:groovy.lang.GroovyRuntimeException:找不到匹配的构造函数:voidjava.util.LinkedHashMap,java.util.LinkedHashMap您是对的,很抱歉,我在编写示例时没有执行它。只要将loppy方法的实现分解为两个步骤,事情就会变得简单。我更新了测试示例并添加了修改过的loppy方法。也许我不清楚我的观点:我需要弄清楚在使用sql.eachRow时如何测试闭包,因为我无法修改loppy方法。这不是我的代码,但我需要测试它!我从没想过这会很难。有没有其他groovy模拟框架可以轻松实现这一点?据我所知,spock并不依赖于它自己的模拟框架。闭包是匿名函数。我认为没有一种方法可以在不重构的情况下对发布的实现进行单元测试。您可以检查Sql对象的交互,如1*Sql.eachRow'select x from y',这意味着不指定第二个参数的调用。如果您在测试中编写闭包,它将以两种方式失败:1。与闭包实例的交互将不同;第二。您将测试mock,而不是实现。您可以重构或使用集成测试,除非您愿意模拟Sql的底层数据源,但这似乎很荒谬