【问题标题】:grails unit testing with multiple datasource使用多个数据源进行 grails 单元测试
【发布时间】:2015-02-13 06:47:55
【问题描述】:

我正在尝试为具有以下结构的 grails 控制器编写单元测试用例:

class MyController{

    def save(){
         def myDomain = new MyDomain(params)
         business validation 1
         business validation 2
         myDomain.writedatasource.save()
         business validation 3
         business validation 4
    }
}

由于单元测试不加载 Datasource.groovy,writedatasource 在单元测试期间不可用,因此“业务验证 3”和“业务验证 4”的测试用例失败,因为我得到

groovy.lang.MissingPropertyException:没有这样的属性:类的writedatasource:MyDomain

如何修改我的测试用例来测试验证场景 3 和 4?

测试用例很简单,如下所示:

void testSave(){
    ...setup...
    controller.save()
    assert conditions
    ....

}

【问题讨论】:

  • 有趣的问题。如果您编写集成测试,您将不得不模拟数据源甚至更好
  • 同意集成测试肯定会完成这项工作,但我们有几个这样的场景实例(几乎 80% 的组件),所以如果我们为每个场景编写集成测试,那么基本上不会有任何单元测试。即使我们模拟数据源,你如何将它附加到 myDomain 以便控制器可以使用它来使 myDomain.writedatasource.save() 工作。

标签: unit-testing grails


【解决方案1】:

不确定这是否能成功,但您可以尝试:

def 'the controller call the save method in writedatasource'() {
    given:
        def calls = 0
    and:
        def myDomainInstance = new MyDomain()
    and:
        MyDomain.metaClass.getWritedatasource = { new Object() }
    and:
        Object.metaClass.save = { Map attrs ->
            calls++
        }
    when:
        controller.save()
    then:
        calls == 1
}

但是你唯一要做的就是测试 save 是在 writedatasource 下调用的,所以最好还有一个集成测试。如果这成功了,请在http://grails.1312388.n4.nabble.com/Mocking-in-a-unit-test-a-domain-object-multiple-datasources-td4646054.html 上回答,因为他们似乎有同样的问题。

【讨论】:

  • 我刚刚意识到我举了一个使用 Spock 的例子,但我认为你可以采取其中的重要部分。
  • 感谢您的帮助。我在字符串上尝试元类,但新对象效果更好。以下是我使用的: Object.metaClass.save = { args -> return true } myDomain.metaClass.rmprw = new Object()
  • 只是为了避免混淆,它是 writedatasource 而不是 rmprw。 Object.metaClass.save = { args -> return true } myDomain.metaClass.writedatasource = new Object()
【解决方案2】:

解决原始问题:

我遇到了这个问题,我不得不模拟很多很多的方法,而不仅仅是在多个域上“保存”。所以相反,我覆盖了我的域的 getter 以简单地返回调用域实例:

def setup() {
    [MyDomain1, MyDomain2].each { Class clazz ->
        mockDomain(clazz)
        clazz.metaClass.getWritedatasource = { 
            return delegate
        }
        clazz.metaClass.'static'.getWritedatasource = {
            return delegate
        }
    }
}

这也使我免于包含@DirtiesRuntime,因为我没有更新任何我想要清理的元类。

最重要的是,无论调用什么数据源,无论是域类还是实例,都应该使用来自 mockDomain 的 GORM 装饰,这意味着我不必模拟任何 GORM 方法。

如果您想要动态数据源怎么办?

在我的特定情况下,数据源是可配置的,并且可能会根据环境而变化。如果您遇到类似情况,可以在域映射中进行配置:

static mapping = {
    datasources    Holders.grailsApplication?.config.dynamic?.datasources
    ...
}

其中 dynamic.datasources 是数据源名称的数组。然后,在测试设置中:

def setup() {
    grailsApplication.config.dynamic.datasources = ['foo', 'bar']
    [MyDomain1, MyDomain2].each { Class clazz ->
        mockDomain(clazz)
        grailsApplication.config.dynamic.datasources.each{
            clazz.metaClass."get${it.capitalize()}" = { 
                return delegate
            }
            clazz.metaClass.'static'."get${it.capitalize()}" = { 
                return delegate
            }
        }
    }
}

【讨论】:

  • 很好的解决方案,虽然在我正确定义带有注释的设置块之前对我不起作用。 @Before void setup() {...}
猜你喜欢
  • 2016-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-17
  • 2017-04-05
  • 2012-05-06
相关资源
最近更新 更多