Spring 如何在用于单元测试的grails服务中使用依赖项注入有选择地设置属性

Spring 如何在用于单元测试的grails服务中使用依赖项注入有选择地设置属性,spring,unit-testing,grails,dependency-injection,Spring,Unit Testing,Grails,Dependency Injection,编辑:请让我澄清一下,我想问的是如何在Grails中使用Spring依赖项注入实现这一点,而不是Grails的元类功能或new() 我有一个用于分析日志文件的grails服务。在服务中,我使用当前时间处理很多事情。对于单元测试,我有几个使用此服务解析的示例日志文件。很明显,这里面有时间 我希望在单元测试期间,我的服务认为当前时间不超过示例日志文件中最后一条日志语句之后的几个小时 因此,我愿意: class MyService { def currentDate = { -> new D

编辑:请让我澄清一下,我想问的是如何在Grails中使用Spring依赖项注入实现这一点,而不是Grails的元类功能或new()

我有一个用于分析日志文件的grails服务。在服务中,我使用当前时间处理很多事情。对于单元测试,我有几个使用此服务解析的示例日志文件。很明显,这里面有时间

我希望在单元测试期间,我的服务认为当前时间不超过示例日志文件中最后一条日志语句之后的几个小时

因此,我愿意:


class MyService {
    def currentDate = { -> new Date() }

    def doSomeStuff() {
        // need to know when is "right now"
        Date now = currentDate()
    }
}
所以,我想能够做的是将currentDate注入或设置为其他硬编码时间,如

currentDate = { -> new Date(1308619647140) }
在我的单元测试中,有没有一种方法可以用mockwhich方法来实现这一点?这种东西在GoogleGuice上非常简单,但我不知道在春天怎么做

当我在谷歌上搜索“grails依赖注入”时,我发现的都是


class SomeController {
  // wow look how amazing this is, it's injected automatically!!
  // isn't spring incredible OMG!
  def myService
}
这让我感觉到我不必输入新的…()

当环境等于测试时,我在哪里告诉它,然后执行以下操作:


currentDate = { -> new Date(1308619647140) }
import grails.util.GrailsUtil
// Place your Spring DSL code here
beans = {
    if (GrailsUtil.environment == 'test') {
        println ">>> test env"
        myService(MyService) {
            timeNow = { -> new Date(1308486447140) }
        }
    } else {
        println ">>> not test env"
        myService(MyService) {
            timeNow = { -> new Date() }
        }
    }
}
我只是被困在测试中手动设置这个属性吗


我更愿意不必创建“timeService”,因为考虑到我只需要一个微小的更改,这似乎很愚蠢。

Groovy是一种动态语言,因此它允许您几乎按照自己的要求进行操作:

class MyServiceTests extends GrailsUnitTestCase {
    def testDoSomeStuff() {
        def service = new MyService()
        service.currentDate = { -> new Date(1308619647140) }

        // assert something on service.doSomeStuff()
    }
}
请记住,这只会修改
服务
实例,而不是类。如果需要修改类,则需要使用
元类
。看一看旁边


另一个选项是将当前日期作为
doSomeStuff()
的参数。这样,您就不需要修改服务实例。

因为Groovy是动态的,所以您可以从服务中删除currentDate()方法,并用适合您需要的方法替换它。您可以在测试设置期间的运行时执行此操作

在实例化MyService实例之前,请执行以下代码:

MyService.metaClass.currentDate << {-> new Date(1308619647140) }
MyService.metaClass.currentDate新日期(1308619647140)}
通过这种方式,您可以在所有测试中获得一致的行为

但是,如果愿意,您可以通过执行相同技巧的闭包覆盖实例方法

让我知道进展如何


Vincent Giguère

谢谢大家的帮助。在本例中,我能想到的使用SpringDI的最佳解决方案是在

resources.groovy

以下是我发现的两种解决方案:
1:如果我想在任何地方交换timeNowService以进行测试:

import grails.util.GrailsUtil

// Place your Spring DSL code here
beans = {
    if (GrailsUtil.environment == 'test') {
        println ">>> test env"
        timeNowService(TimeNowMockService)
    } else {
        println ">>> not test env"
        timeNowService(TimeNowService)
    }
}

2:如果我只希望此更改适用于此特定服务,我可以这样做:

import grails.util.GrailsUtil

// Place your Spring DSL code here
beans = {

    if (GrailsUtil.environment == 'test') {
        println ">>> test env"
        time1(TimeNowMockService)
    } else {
        println ">>> not test env"
        time1(TimeNowService)
    }
    myService(MyService) {
        diTest = 'hello 2'
        timeNowService = ref('time1')
    }
}

在任何一种情况下,我都会通过拨打
timeNowService.now()
。 对我来说,一件奇怪的、非常令人沮丧的事情是我不能这样做:

import grails.util.GrailsUtil
// Place your Spring DSL code here
beans = {
    if (GrailsUtil.environment == 'test') {
        println ">>> test env"
        myService(MyService) {
            timeNow = { -> new Date(1308486447140) }
        }
    } else {
        println ">>> not test env"
        myService(MyService) {
            timeNow = { -> new Date() }
        }
    }
}
事实上,当我尝试这样做时,我也有一个伪值,比如
dummy='hello 2'
,然后在myService类本身中有一个默认值
dummy='hello'
。当我在第三个例子中也设置了虚拟值时,它默默地没有设置,显然b/c timeNow私下里搞砸了什么

我很想知道是否有人能解释这失败的原因


感谢大家的帮助,很抱歉不耐烦…

感谢大家迄今为止的答案,但我真的很想知道如何使用Spring/Grails依赖项注入b/c实现这一点,因为在我看来,DI的全部目的就是这种上下文行为。是否真的没有一种合理的方法来实现这一点——根据依赖项注入的环境(测试、生产或开发)加载不同的属性?如果您不能做到这一点,那么它的实际用途是什么?Grails不会在单元测试期间注入依赖项。它不会在SUT类的集成测试期间注入它们。看见也许这就是你要找的?检查Spring DSL上的部分。再说一次,这在单元测试期间是行不通的。也许你可以为你想要实现的东西发布等价的GoogleGuice代码?