【问题标题】:Groovy/Spock: specify which values are passed into a closure of a mockGroovy/Spock:指定将哪些值传递到模拟的闭包中
【发布时间】:2013-11-30 20:36:58
【问题描述】:

首先,这是我与 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"() {
    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")
}

这只是我想出的众多变体之一......直到现在都没有奏效。文档没有多大帮助,因为我似乎没有找到好的关键字来搜索。

谢谢

【问题讨论】:

  • 您模拟了 sql 对象并且没有为 eachRow 方法定义行为。不要指望那个模拟自己知道该做什么。但是,即使您已经定义(模拟)了您将测试模拟而不是实现的方法 - 这是毫无意义的。您可以尝试使用 Spy 而不是 Mock,但此时您的实现还没有“测试友好”的设计。
  • 如何定义 Sql.eachRow 的行为?我发现很多例子告诉我如何定义返回值,但是如何定义在第一次、第二次、第三次调用时传递给闭包的内容?您能否提供所需的代码?非常感谢!

标签: unit-testing groovy mocking spock


【解决方案1】:

首先,您需要考虑要测试什么。 “Loppy”似乎不够明确。 我不确定所以只是粗略地说,将您的测试分成两个功能方法,如下所示:

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.eachRow(String, Closure) 返回 void 不能使用类 'java.util.ArrayList' 强制转换对象 '[{x=a}, {x=b}]'由于:groovy.lang.GroovyRuntimeException:找不到匹配的构造函数:void(java.util.LinkedHashMap,java.util.LinkedHashMap)
  • 你是对的,对不起,我写的例子没有执行。只需将 loppy 方法的实现分解为两个步骤,事情就会变得更容易。我更新了测试示例并添加了更改的 loppy 方法。
  • 也许我并不清楚我的观点:我需要弄清楚在使用sql.eachRow 时如何测试闭包,因为我无法更改 loppy 方法。这不是我的代码,但我需要测试它!我从没想过这会很难。有没有办法使用其他任何 groovy 模拟框架轻松地做到这一点?据我了解,spock 并不依赖于它自己的模拟框架。
  • 闭包是匿名函数。我认为没有一种方法可以在不重构的情况下对发布的实现进行单元测试。您可以像1 * sql.eachRow('select x from y', _) 那样检查 Sql 对象的交互,这意味着在没有指定第二个参数的情况下调用。如果您在测试中编写 Closure,它将以两种方式失败:1st。作为闭包实例的交互将不一样;第二。无论如何,您都会测试模拟而不是实现。你可以重构也可以使用集成测试(除非你愿意mock Sql 的底层DataSource,但这看起来很荒谬)
猜你喜欢
  • 2016-05-10
  • 2020-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-27
  • 1970-01-01
  • 2019-08-29
  • 2019-07-22
相关资源
最近更新 更多