使用闭包对 GORM 调用进行单元测试

Unit testing a GORM call with a closure

我有一个 Grails 服务可以像这样执行 where 查询:

List<Car> search(Long makeId = null) {
    Car.where {
        join("make")
        if(makeId) {
            make.id == makeId
        }
    }.findAll()
}

我正在尝试像这样使用 Spock 对其进行单元测试:

def setup() {
    GroovyMock(Car, global: true)
}

void "test search"() {
    when:
        service.search()
    then:
        1 * Car.where {}
}

但是,我似乎找不到测试闭包内容的方法。

我可以通过验证 1 * Car.where(_) 让测试通过,但是我如何对闭包的内容进行断言,即 join 被调用并且 make.id只在需要时指定约束?

您可以将闭包的委托设置为 DetachedCriteria 的 Mock 以对其进行断言。 DetachedCriteria 是 gorm 中用于构建查询的主要class。

示例:

given: 'Mocking DetachedCriteria'
DetachedCriteria detachedCriteriaMock = Mock(DetachedCriteria)
and: 'Just to avoid nullPointerException when findAll() call happens on service'
1 * detachedCriteriaMock.iterator() >> [].listIterator()
when:
service.search(1L)
then:
// Capture the argument
1 * Car.where(_) >>  { args ->
    args[0].delegate = detachedCriteriaMock
    args[0].call()

    return detachedCriteriaMock
}

// Join is a method on detached criteria
1 * detachedCriteriaMock.join('make')
// Make is an association, so detachedCriteria uses the methodMissing to find the property.
// In this case, we call the closure setting the delegate to the mock
1 * detachedCriteriaMock.methodMissing('make', _) >> { args ->
    // args[1] is the list of arguments.
    // args[1][0] is the closure itself passed to detachedCriteria
    args[1][0].delegate = detachedCriteriaMock
    args[1][0].call()
}
// If id is passed, it must compare (eq method) with value 1
1 * detachedCriteriaMock.eq('id', 1L)